From cff179f1ab52e0646e150d4d430dbfb2a0c2da93 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Mar 2003 16:59:16 +0000 Subject: [SERIAL] Overhaul 8250_pci.c - Add local copy of flags for describing port characteristics. - Add "quirk" handling to sort out board-specific initialisation, per-port setup, and finalisation. - Remove quirk information from struct pci_board array, thereby making entries more generic. - Initialise struct pci_board array using explicit indicies - adding to the readability, and making sure that the right entries are in the correct location in the table. - Ensure we tell the serial layer to claim resources, and share interrupts. PCI interrupts are sharable, and need to be so that multi-function cardbus cards will work. - Only remap each PCI memory BAR once. - Add pci serial driver to the tty devclass (this requires the patch to make tty_io.c register the tty devclass early.) Tested by CaT and myself. --- drivers/serial/8250_pci.c | 1723 ++++++++++++++++++++++++++++++++------------- 1 file changed, 1240 insertions(+), 483 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 240b79df5794..b0711e53f471 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -20,8 +20,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -30,174 +31,213 @@ #include "8250.h" +/* + * Definitions for PCI support. + */ +#define FL_BASE_MASK 0x0007 +#define FL_BASE0 0x0000 +#define FL_BASE1 0x0001 +#define FL_BASE2 0x0002 +#define FL_BASE3 0x0003 +#define FL_BASE4 0x0004 +#define FL_GET_BASE(x) (x & FL_BASE_MASK) + +#define FL_IRQ_MASK (0x0007 << 4) +#define FL_IRQBASE0 (0x0000 << 4) +#define FL_IRQBASE1 (0x0001 << 4) +#define FL_IRQBASE2 (0x0002 << 4) +#define FL_IRQBASE3 (0x0003 << 4) +#define FL_IRQBASE4 (0x0004 << 4) +#define FL_GET_IRQBASE(x) ((x & FL_IRQ_MASK) >> 4) + +/* Use successive BARs (PCI base address registers), + else use offset into some specified BAR */ +#define FL_BASE_BARS 0x0008 + +/* Use the irq resource table instead of dev->irq */ +#define FL_IRQRESOURCE 0x0080 + +/* Use the Base address register size to cap number of ports */ +#define FL_REGION_SZ_CAP 0x0100 -#ifndef IS_PCI_REGION_IOPORT -#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_IO) -#endif -#ifndef IS_PCI_REGION_IOMEM -#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ - IORESOURCE_MEM) -#endif -#ifndef PCI_IRQ_RESOURCE -#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) -#endif - -#ifndef pci_get_subvendor -#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) -#define pci_get_subdevice(dev) ((dev)->subsystem_device) -#endif - -struct serial_private { - unsigned int nr; - struct pci_board *board; - int line[0]; +struct pci_board { + unsigned int flags; + unsigned int num_ports; + unsigned int base_baud; + unsigned int uart_offset; + unsigned int reg_shift; + unsigned int first_offset; }; /* - * init_fn returns: + * init function returns: * > 0 - number of ports * = 0 - use board->num_ports * < 0 - error */ -struct pci_board { - int flags; - int num_ports; - int base_baud; - int uart_offset; - int reg_shift; - int (*init_fn)(struct pci_dev *dev, int enable); - int first_uart_offset; +struct pci_serial_quirk { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; + int (*init)(struct pci_dev *dev); + int (*setup)(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx); + void (*exit)(struct pci_dev *dev); +}; + +#define PCI_NUM_BAR_RESOURCES 6 + +struct serial_private { + unsigned int nr; + void *remapped_bar[PCI_NUM_BAR_RESOURCES]; + struct pci_serial_quirk *quirk; + int line[0]; }; +static void moan_device(const char *str, struct pci_dev *dev) +{ + printk(KERN_WARNING "%s: %s\n" + KERN_WARNING "Please send the output of lspci -vv, this\n" + KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + KERN_WARNING "manufacturer and name of serial board or\n" + KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n", + dev->slot_name, str, dev->vendor, dev->device, + dev->subsystem_vendor, dev->subsystem_device); +} + static int -get_pci_port(struct pci_dev *dev, struct pci_board *board, - struct serial_struct *req, int idx) +setup_port(struct pci_dev *dev, struct serial_struct *req, + int bar, int offset, int regshift) { - unsigned long port; - int base_idx; - int max_port; - int offset; + struct serial_private *priv = pci_get_drvdata(dev); + unsigned long port, len; - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; + if (bar >= PCI_NUM_BAR_RESOURCES) + return -EINVAL; - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { + port = pci_resource_start(dev, bar); + len = pci_resource_len(dev, bar); - /* - * Timedia/SUNIX uses a mixture of BARs and offsets - * Ugh, this is ugly as all hell --- TYT - */ - if (dev->vendor == PCI_VENDOR_ID_TIMEDIA) - switch(idx) { - case 0: - base_idx = 0; - break; - case 1: - base_idx = 0; - offset = 8; - break; - case 2: - base_idx = 1; - break; - case 3: - base_idx = 1; - offset = 8; - break; - case 4: /* BAR 2 */ - case 5: /* BAR 3 */ - case 6: /* BAR 4 */ - case 7: /* BAR 5 */ - base_idx = idx - 2; - } + if (!priv->remapped_bar[bar]) + priv->remapped_bar[bar] = ioremap(port, len); + if (!priv->remapped_bar[bar]) + return -ENOMEM; - /* AFAVLAB uses a different mixture of BARs and offsets */ - /* Not that ugly ;) -- HW */ - if (dev->vendor == PCI_VENDOR_ID_AFAVLAB && idx >= 4) { - base_idx = 4; - offset = (idx - 4) * 8; + req->io_type = UPIO_MEM; + req->iomap_base = port; + req->iomem_base = priv->remapped_bar[bar] + offset; + req->iomem_reg_shift = regshift; + } else { + port = pci_resource_start(dev, bar) + offset; + req->io_type = UPIO_PORT; + req->port = port; + if (HIGH_BITS_OFFSET) + req->port_high = port >> HIGH_BITS_OFFSET; } + return 0; +} - /* Some Titan cards are also a little weird */ - if (dev->vendor == PCI_VENDOR_ID_TITAN && - (dev->device == PCI_DEVICE_ID_TITAN_400L || - dev->device == PCI_DEVICE_ID_TITAN_800L)) { - switch (idx) { - case 0: base_idx = 1; - break; - case 1: base_idx = 2; - break; - default: - base_idx = 4; - offset = 8 * (idx - 2); - } - } +/* + * AFAVLAB uses a different mixture of BARs and offsets + * Not that ugly ;) -- HW + */ +static int +afavlab_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + bar = FL_GET_BASE(board->flags); + if (idx < 4) + bar += idx; + else + offset += (idx - 4) * board->uart_offset; - /* HP's Diva chip puts the 4th/5th serial port further out, and - * some serial ports are supposed to be hidden on certain models. - */ - if (dev->vendor == PCI_VENDOR_ID_HP && - dev->device == PCI_DEVICE_ID_HP_DIVA) { - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - if (idx == 3) - idx++; - break; - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - if (idx > 0) - idx++; - if (idx > 2) - idx++; - break; - } - if (idx > 2) { - offset = 0x18; - } + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * HP's Remote Management Console. The Diva chip came in several + * different versions. N-class, L2000 and A500 have two Diva chips, each + * with 3 UARTs (the third UART on the second chip is unused). Superdome + * and Keystone have one Diva chip with 3 UARTs. Some later machines have + * one Diva chip, but it has been expanded to 5 UARTs. + */ +static int __devinit pci_hp_diva_init(struct pci_dev *dev) +{ + int rc = 0; + + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_TOSCA1: + case PCI_DEVICE_ID_HP_DIVA_HALFDOME: + case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + rc = 3; + break; + case PCI_DEVICE_ID_HP_DIVA_TOSCA2: + rc = 2; + break; + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + rc = 4; + break; + case PCI_DEVICE_ID_HP_DIVA_POWERBAR: + rc = 1; + break; } - port = pci_resource_start(dev, base_idx) + offset; + return rc; +} - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); +/* + * HP's Diva chip puts the 4th/5th serial port further out, and + * some serial ports are supposed to be hidden on certain models. + */ +static int +pci_hp_diva_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int offset = board->first_offset; + unsigned int bar = FL_GET_BASE(board->flags); - if (IS_PCI_REGION_IOPORT(dev, base_idx)) { - req->port = port; - if (HIGH_BITS_OFFSET) - req->port_high = port >> HIGH_BITS_OFFSET; - else - req->port_high = 0; - return 0; + switch (dev->subsystem_device) { + case PCI_DEVICE_ID_HP_DIVA_MAESTRO: + if (idx == 3) + idx++; + break; + case PCI_DEVICE_ID_HP_DIVA_EVEREST: + if (idx > 0) + idx++; + if (idx > 2) + idx++; + break; } - req->io_type = SERIAL_IO_MEM; - req->iomap_base = port; - req->iomem_base = ioremap(port, board->uart_offset); - if (req->iomem_base == NULL) - return -ENOMEM; - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return 0; + if (idx > 2) + offset = 0x18; + + offset += idx * board->uart_offset; + + return setup_port(dev, req, bar, offset, board->reg_shift); } -static _INLINE_ int -get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +/* + * Added for EKF Intel i960 serial boards + */ +static int __devinit pci_inteli960ni_init(struct pci_dev *dev) { - int base_idx; + unsigned long oldval; - if ((board->flags & SPCI_FL_IRQRESOURCE) == 0) - return dev->irq; + if (!(dev->subsystem_device & 0x1000)) + return -ENODEV; - base_idx = SPCI_FL_GET_IRQBASE(board->flags); - if (board->flags & SPCI_FL_IRQ_TABLE) - base_idx += idx; - - return PCI_IRQ_RESOURCE(dev, base_idx); + /* is firmware started? */ + pci_read_config_dword(dev, 0x44, (void*) &oldval); + if (oldval == 0x00001000L) { /* RESET value */ + printk(KERN_DEBUG "Local i960 firmware missing"); + return -ENODEV; + } + return 0; } /* @@ -206,26 +246,29 @@ 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 * mapped memory. */ -static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) +static int __devinit pci_plx9050_init(struct pci_dev *dev) { - u8 *p, irq_config = 0; - - if (enable) { - irq_config = 0x41; - if (dev->vendor == PCI_VENDOR_ID_PANACOM) - irq_config = 0x43; - if ((dev->vendor == PCI_VENDOR_ID_PLX) && - (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { - /* - * As the megawolf cards have the int pins active - * high, and have 2 UART chips, both ints must be - * enabled on the 9050. Also, the UARTS are set in - * 16450 mode by default, so we have to enable the - * 16C950 'enhanced' mode so that we can use the - * deep FIFOs - */ - irq_config = 0x5b; - } + u8 *p, irq_config; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) { + moan_device("no memory in bar 0", dev); + return 0; + } + + irq_config = 0x41; + if (dev->vendor == PCI_VENDOR_ID_PANACOM) + irq_config = 0x43; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_ROMULUS)) { + /* + * As the megawolf cards have the int pins active + * high, and have 2 UART chips, both ints must be + * enabled on the 9050. Also, the UARTS are set in + * 16450 mode by default, so we have to enable the + * 16C950 'enhanced' mode so that we can use the + * deep FIFOs + */ + irq_config = 0x5b; } /* @@ -245,6 +288,27 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) return 0; } +static void __devexit pci_plx9050_exit(struct pci_dev *dev) +{ + u8 *p; + + if ((pci_resource_flags(dev, 0) & IORESOURCE_MEM) == 0) + return; + + /* + * disable interrupts + */ + p = ioremap(pci_resource_start(dev, 0), 0x80); + if (p != NULL) { + writel(0, p + 0x4c); + + /* + * Read the register back to ensure that it took effect. + */ + readl(p + 0x4c); + iounmap(p); + } +} /* * SIIG serial cards have an PCI interface chip which also controls @@ -270,23 +334,20 @@ static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable) #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) -int pci_siig10x_fn(struct pci_dev *dev, int enable) +static int pci_siig10x_init(struct pci_dev *dev) { u16 data, *p; - if (!enable) - return 0; - switch (dev->device & 0xfff8) { - case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ - data = 0xffdf; - break; - case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ - data = 0xf7ff; - break; - default: /* 1S1P, 4S */ - data = 0xfffb; - break; + case PCI_DEVICE_ID_SIIG_1S_10x: /* 1S */ + data = 0xffdf; + break; + case PCI_DEVICE_ID_SIIG_2S_10x: /* 2S, 2S1P */ + data = 0xf7ff; + break; + default: /* 1S1P, 4S */ + data = 0xfffb; + break; } p = ioremap(pci_resource_start(dev, 0), 0x80); @@ -294,22 +355,18 @@ int pci_siig10x_fn(struct pci_dev *dev, int enable) return -ENOMEM; writew(readw((unsigned long) p + 0x28) & data, (unsigned long) p + 0x28); + readw((unsigned long)p + 0x28); iounmap(p); return 0; } -EXPORT_SYMBOL(pci_siig10x_fn); - #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) -int pci_siig20x_fn(struct pci_dev *dev, int enable) +static int pci_siig20x_init(struct pci_dev *dev) { u8 data; - if (!enable) - return 0; - /* Change clock frequency for the first UART. */ pci_read_config_byte(dev, 0x6f, &data); pci_write_config_byte(dev, 0x6f, data & 0xef); @@ -323,28 +380,25 @@ int pci_siig20x_fn(struct pci_dev *dev, int enable) return 0; } -EXPORT_SYMBOL(pci_siig20x_fn); - -/* Added for EKF Intel i960 serial boards */ -static int __devinit pci_inteli960ni_fn(struct pci_dev *dev, int enable) +int pci_siig10x_fn(struct pci_dev *dev, int enable) { - unsigned long oldval; - - if (!(pci_get_subdevice(dev) & 0x1000)) - return -ENODEV; + int ret = 0; + if (enable) + ret = pci_siig10x_init(dev); + return ret; +} - if (!enable) /* is there something to deinit? */ - return 0; - - /* is firmware started? */ - pci_read_config_dword(dev, 0x44, (void*) &oldval); - if (oldval == 0x00001000L) { /* RESET value */ - printk(KERN_DEBUG "Local i960 firmware missing"); - return -ENODEV; - } - return 0; +int pci_siig20x_fn(struct pci_dev *dev, int enable) +{ + int ret = 0; + if (enable) + ret = pci_siig20x_init(dev); + return ret; } +EXPORT_SYMBOL(pci_siig10x_fn); +EXPORT_SYMBOL(pci_siig20x_fn); + /* * Timedia has an explosion of boards, and to avoid the PCI table from * growing *huge*, we use this function to collapse some 70 entries @@ -385,78 +439,453 @@ static struct timedia_struct { { 0, 0 } }; -static int __devinit pci_timedia_fn(struct pci_dev *dev, int enable) +static int __devinit pci_timedia_init(struct pci_dev *dev) { - int i, j; unsigned short *ids; - - if (!enable) - return 0; + int i, j; for (i = 0; timedia_data[i].num; i++) { ids = timedia_data[i].ids; for (j = 0; ids[j]; j++) - if (pci_get_subdevice(dev) == ids[j]) + if (dev->subsystem_device == ids[j]) return timedia_data[i].num; } return 0; } /* - * HP's Remote Management Console. The Diva chip came in several - * different versions. N-class, L2000 and A500 have two Diva chips, each - * with 3 UARTs (the third UART on the second chip is unused). Superdome - * and Keystone have one Diva chip with 3 UARTs. Some later machines have - * one Diva chip, but it has been expanded to 5 UARTs. + * Timedia/SUNIX uses a mixture of BARs and offsets + * Ugh, this is ugly as all hell --- TYT */ -static int __devinit pci_hp_diva(struct pci_dev *dev, int enable) +static int +pci_timedia_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) { - int rc = 0; - - if (!enable) - return 0; + unsigned int bar = 0, offset = board->first_offset; - switch (dev->subsystem_device) { - case PCI_DEVICE_ID_HP_DIVA_TOSCA1: - case PCI_DEVICE_ID_HP_DIVA_HALFDOME: - case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: - case PCI_DEVICE_ID_HP_DIVA_EVEREST: - rc = 3; + switch (idx) { + case 0: + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_TOSCA2: - rc = 2; + case 1: + offset = board->uart_offset; + bar = 0; break; - case PCI_DEVICE_ID_HP_DIVA_MAESTRO: - rc = 4; - break; - case PCI_DEVICE_ID_HP_DIVA_POWERBAR: - rc = 1; + case 2: + bar = 1; break; + case 3: + offset = board->uart_offset; + bar = 1; + case 4: /* BAR 2 */ + case 5: /* BAR 3 */ + case 6: /* BAR 4 */ + case 7: /* BAR 5 */ + bar = idx - 2; } - return rc; + return setup_port(dev, req, bar, offset, board->reg_shift); } +/* + * Some Titan cards are also a little weird + */ +static int +titan_400l_800l_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset; + + switch (idx) { + case 0: + bar = 1; + break; + case 1: + bar = 2; + break; + default: + bar = 4; + offset = (idx - 2) * board->uart_offset; + } + + return setup_port(dev, req, bar, offset, board->reg_shift); +} -static int __devinit pci_xircom_fn(struct pci_dev *dev, int enable) +static int __devinit pci_xircom_init(struct pci_dev *dev) { __set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/10); return 0; } +static int +pci_default_setup(struct pci_dev *dev, struct pci_board *board, + struct serial_struct *req, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + maxnr = (pci_resource_len(dev, bar) - board->uart_offset) / + (8 << board->reg_shift); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(dev, req, bar, offset, board->reg_shift); +} + +/* + * Master list of serial port init/setup/exit quirks. + * This does not describe the general nature of the port. + * (ie, baud base, number and location of ports, etc) + * + * This list is ordered alphabetically by vendor then device. + * Specific entries must come before more generic entries. + */ +static struct pci_serial_quirk pci_serial_quirks[] = { + /* + * AFAVLAB cards. + * It is not clear whether this applies to all products. + */ + { + .vendor = PCI_VENDOR_ID_AFAVLAB, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = afavlab_setup, + }, + /* + * HP Diva + */ + { + .vendor = PCI_VENDOR_ID_HP, + .device = PCI_DEVICE_ID_HP_DIVA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_hp_diva_init, + .setup = pci_hp_diva_setup, + }, + /* + * Intel + */ + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_80960_RP, + .subvendor = 0xe4bf, + .subdevice = PCI_ANY_ID, + .init = pci_inteli960ni_init, + .setup = pci_default_setup, + }, + /* + * Panacom + */ + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_QUADMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PANACOM, + .device = PCI_DEVICE_ID_PANACOM_DUALMODEM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * PLX + */ + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_SUBVENDOR_ID_KEYSPAN, + .subdevice = PCI_SUBDEVICE_ID_KEYSPAN_SX2, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_ROMULUS, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_ROMULUS, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = __devexit_p(pci_plx9050_exit), + }, + /* + * SIIG cards. + * It is not clear whether these could be collapsed. + */ + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_10x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig10x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_1S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_2S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_550, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_650, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + { + .vendor = PCI_VENDOR_ID_SIIG, + .device = PCI_DEVICE_ID_SIIG_4S_20x_850, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_siig20x_init, + .setup = pci_default_setup, + }, + /* + * Titan cards + */ + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_400L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + { + .vendor = PCI_VENDOR_ID_TITAN, + .device = PCI_DEVICE_ID_TITAN_800L, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = titan_400l_800l_setup, + }, + /* + * Timedia cards + */ + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_DEVICE_ID_TIMEDIA_1889, + .subvendor = PCI_VENDOR_ID_TIMEDIA, + .subdevice = PCI_ANY_ID, + .init = pci_timedia_init, + .setup = pci_timedia_setup, + }, + { + .vendor = PCI_VENDOR_ID_TIMEDIA, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_timedia_setup, + }, + /* + * Xircom cards + */ + { + .vendor = PCI_VENDOR_ID_XIRCOM, + .device = PCI_DEVICE_ID_XIRCOM_X3201_MDM, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .init = pci_xircom_init, + .setup = pci_default_setup, + }, + /* + * Default "match everything" terminator entry + */ + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_default_setup, + } +}; + +static inline int quirk_id_matches(u32 quirk_id, u32 dev_id) +{ + return quirk_id == PCI_ANY_ID || quirk_id == dev_id; +} + +static struct pci_serial_quirk *find_quirk(struct pci_dev *dev) +{ + struct pci_serial_quirk *quirk; + + for (quirk = pci_serial_quirks; ; quirk++) + if (quirk_id_matches(quirk->vendor, dev->vendor) && + quirk_id_matches(quirk->device, dev->device) && + quirk_id_matches(quirk->subvendor, dev->subsystem_vendor) && + quirk_id_matches(quirk->subdevice, dev->subsystem_device)) + break; + return quirk; +} + +static _INLINE_ int +get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) +{ + int base_idx; + + if ((board->flags & FL_IRQRESOURCE) == 0) + return dev->irq; + + base_idx = FL_GET_IRQBASE(board->flags); + + if (base_idx > DEVICE_COUNT_IRQ) + return 0; + + return dev->irq_resource[base_idx].start; +} + /* * This is the configuration table for all of the PCI serial boards * which we support. It is directly indexed by the pci_board_num_t enum * value, which is encoded in the pci_device_id PCI probe table's * driver_data member. + * + * The makeup of these names are: + * pbn_bn{_bt}_n_baud + * + * bn = PCI BAR number + * bt = Index using PCI BARs + * n = number of serial ports + * baud = baud rate + * + * Please note: in theory if n = 1, _bt infix should make no difference. + * ie, pbn_b0_1_115200 is the same as pbn_b0_bt_1_115200 */ enum pci_board_num_t { - pbn_b0_1_115200, pbn_default = 0, + pbn_b0_1_115200, pbn_b0_2_115200, pbn_b0_4_115200, + pbn_b0_5_115200, pbn_b0_1_921600, pbn_b0_2_921600, @@ -465,171 +894,465 @@ enum pci_board_num_t { pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, pbn_b0_bt_8_115200, + pbn_b0_bt_1_460800, pbn_b0_bt_2_460800, + pbn_b0_bt_1_921600, + pbn_b0_bt_2_921600, + pbn_b0_bt_4_921600, + pbn_b0_bt_8_921600, + pbn_b1_1_115200, pbn_b1_2_115200, pbn_b1_4_115200, pbn_b1_8_115200, + pbn_b1_1_921600, pbn_b1_2_921600, pbn_b1_4_921600, pbn_b1_8_921600, + pbn_b1_bt_2_921600, + pbn_b1_2_1382400, pbn_b1_4_1382400, pbn_b1_8_1382400, pbn_b2_1_115200, pbn_b2_8_115200, + + pbn_b2_1_460800, pbn_b2_4_460800, pbn_b2_8_460800, pbn_b2_16_460800, + + pbn_b2_1_921600, pbn_b2_4_921600, pbn_b2_8_921600, pbn_b2_bt_1_115200, pbn_b2_bt_2_115200, pbn_b2_bt_4_115200, + pbn_b2_bt_2_921600, + pbn_b2_bt_4_921600, + pbn_b3_4_115200, + pbn_b3_8_115200, + + /* + * Board-specific versions. + */ pbn_panacom, pbn_panacom2, pbn_panacom4, pbn_plx_romulus, pbn_oxsemi, - pbn_timedia, pbn_intel_i960, pbn_sgi_ioc3, - pbn_hp_diva, pbn_nec_nile4, - - pbn_dci_pccom4, - pbn_dci_pccom8, - - pbn_xircom_combo, - - pbn_siig10x_0, - pbn_siig10x_1, - pbn_siig10x_2, - pbn_siig10x_4, - pbn_siig20x_0, - pbn_siig20x_2, - pbn_siig20x_4, - pbn_computone_4, pbn_computone_6, pbn_computone_8, }; static struct pci_board pci_boards[] __devinitdata = { + [pbn_default] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_2_115200] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_4_115200] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_5_115200] = { + .flags = FL_BASE0, + .num_ports = 5, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_1_921600] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_2_921600] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_4_921600] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_2_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b0_bt_8_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b0_bt_2_460800] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b0_bt_1_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_2_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_4_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b0_bt_8_921600] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_1_115200] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_2_115200] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_4_115200] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b1_8_115200] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b1_1_921600] = { + .flags = FL_BASE1, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_2_921600] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_4_921600] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b1_8_921600] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_bt_2_921600] = { + .flags = FL_BASE1|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b1_2_1382400] = { + .flags = FL_BASE1, + .num_ports = 2, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_4_1382400] = { + .flags = FL_BASE1, + .num_ports = 4, + .base_baud = 1382400, + .uart_offset = 8, + }, + [pbn_b1_8_1382400] = { + .flags = FL_BASE1, + .num_ports = 8, + .base_baud = 1382400, + .uart_offset = 8, + }, + + [pbn_b2_1_115200] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_8_115200] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_1_460800] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_4_460800] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_8_460800] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 460800, + .uart_offset = 8, + }, + [pbn_b2_16_460800] = { + .flags = FL_BASE2, + .num_ports = 16, + .base_baud = 460800, + .uart_offset = 8, + }, + + [pbn_b2_1_921600] = { + .flags = FL_BASE2, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_4_921600] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_8_921600] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b2_bt_1_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 1, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_2_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b2_bt_4_115200] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + + [pbn_b2_bt_2_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b2_bt_4_921600] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + + [pbn_b3_4_115200] = { + .flags = FL_BASE3, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, + [pbn_b3_8_115200] = { + .flags = FL_BASE3, + .num_ports = 8, + .base_baud = 115200, + .uart_offset = 8, + }, + /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset + * Entries following this are board-specific. */ - /* Generic serial board, pbn_b0_1_115200, pbn_default */ - { SPCI_FL_BASE0, 1, 115200 }, /* pbn_b0_1_115200, - pbn_default */ - - { SPCI_FL_BASE0, 2, 115200 }, /* pbn_b0_2_115200 */ - { SPCI_FL_BASE0, 4, 115200 }, /* pbn_b0_4_115200 */ - - { SPCI_FL_BASE0, 1, 921600 }, /* pbn_b0_1_921600 */ - { SPCI_FL_BASE0, 2, 921600 }, /* pbn_b0_2_921600 */ - { SPCI_FL_BASE0, 4, 921600 }, /* pbn_b0_4_921600 */ - - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b0_bt_1_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b0_bt_2_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 8, 115200 }, /* pbn_b0_bt_8_115200 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 460800 }, /* pbn_b0_bt_1_460800 */ - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 460800 }, /* pbn_b0_bt_2_460800 */ - - { SPCI_FL_BASE1, 1, 115200 }, /* pbn_b1_1_115200 */ - { SPCI_FL_BASE1, 2, 115200 }, /* pbn_b1_2_115200 */ - { SPCI_FL_BASE1, 4, 115200 }, /* pbn_b1_4_115200 */ - { SPCI_FL_BASE1, 8, 115200 }, /* pbn_b1_8_115200 */ - - { SPCI_FL_BASE1, 2, 921600 }, /* pbn_b1_2_921600 */ - { SPCI_FL_BASE1, 4, 921600 }, /* pbn_b1_4_921600 */ - { SPCI_FL_BASE1, 8, 921600 }, /* pbn_b1_8_921600 */ - - { SPCI_FL_BASE1, 2, 1382400 }, /* pbn_b1_2_1382400 */ - { SPCI_FL_BASE1, 4, 1382400 }, /* pbn_b1_4_1382400 */ - { SPCI_FL_BASE1, 8, 1382400 }, /* pbn_b1_8_1382400 */ - - { SPCI_FL_BASE2, 1, 115200 }, /* pbn_b2_1_115200 */ - { SPCI_FL_BASE2, 8, 115200 }, /* pbn_b2_8_115200 */ - { SPCI_FL_BASE2, 4, 460800 }, /* pbn_b2_4_460800 */ - { SPCI_FL_BASE2, 8, 460800 }, /* pbn_b2_8_460800 */ - { SPCI_FL_BASE2, 16, 460800 }, /* pbn_b2_16_460800 */ - { SPCI_FL_BASE2, 4, 921600 }, /* pbn_b2_4_921600 */ - { SPCI_FL_BASE2, 8, 921600 }, /* pbn_b2_8_921600 */ - - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 1, 115200 }, /* pbn_b2_bt_1_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 115200 }, /* pbn_b2_bt_2_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 115200 }, /* pbn_b2_bt_4_115200 */ - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600 }, /* pbn_b2_bt_2_921600 */ - - { SPCI_FL_BASE2, 2, 921600, /* IOMEM */ /* pbn_panacom */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_panacom2 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_panacom4 */ - 0x400, 7, pci_plx9050_fn }, - { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ - 0x20, 2, pci_plx9050_fn, 0x03 }, + /* + * Panacom - IOMEM + */ + [pbn_panacom] = { + .flags = FL_BASE2, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom2] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + [pbn_panacom4] = { + .flags = FL_BASE2|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x400, + .reg_shift = 7, + }, + + /* I think this entry is broken - the first_offset looks wrong --rmk */ + [pbn_plx_romulus] = { + .flags = FL_BASE2, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x03, + }, /* * 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_BASE_TABLE, 1, 921600, /* pbn_timedia */ - 0, 0, pci_timedia_fn }, - /* EKF addition for i960 Boards form EKF with serial port */ - { SPCI_FL_BASE0, 32, 921600, /* max 256 ports */ /* pbn_intel_i960 */ - 8<<2, 2, pci_inteli960ni_fn, 0x10000}, - { SPCI_FL_BASE0 | SPCI_FL_IRQRESOURCE, /* pbn_sgi_ioc3 */ - 1, 458333, 0, 0, 0, 0x20178 }, - { SPCI_FL_BASE0, 5, 115200, 8, 0, pci_hp_diva, 0 },/* pbn_hp_diva */ + [pbn_oxsemi] = { + .flags = FL_BASE0|FL_REGION_SZ_CAP, + .num_ports = 32, + .base_baud = 115200, + .uart_offset = 8, + }, + + /* + * EKF addition for i960 Boards form EKF with serial port. + * Max 256 ports. + */ + [pbn_intel_i960] = { + .flags = FL_BASE0, + .num_ports = 32, + .base_baud = 921600, + .uart_offset = 8 << 2, + .reg_shift = 2, + .first_offset = 0x10000, + }, + [pbn_sgi_ioc3] = { + .flags = FL_BASE0|FL_IRQRESOURCE, + .num_ports = 1, + .base_baud = 458333, + .uart_offset = 8, + .reg_shift = 0, + .first_offset = 0x20178, + }, /* * NEC Vrc-5074 (Nile 4) builtin UART. */ - { SPCI_FL_BASE0, 1, 520833, /* pbn_nec_nile4 */ - 64, 3, NULL, 0x300 }, - - { SPCI_FL_BASE3, 4, 115200, 8 }, /* pbn_dci_pccom4 */ - { SPCI_FL_BASE3, 8, 115200, 8 }, /* pbn_dci_pccom8 */ - - { SPCI_FL_BASE0, 1, 115200, /* pbn_xircom_combo */ - 0, 0, pci_xircom_fn }, - - { SPCI_FL_BASE2, 1, 460800, /* pbn_siig10x_0 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2, 1, 921600, /* pbn_siig10x_1 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siig10x_2 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE2 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siig10x_4 */ - 0, 0, pci_siig10x_fn }, - { SPCI_FL_BASE0, 1, 921600, /* pbn_siix20x_0 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 921600, /* pbn_siix20x_2 */ - 0, 0, pci_siig20x_fn }, - { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 4, 921600, /* pbn_siix20x_4 */ - 0, 0, pci_siig20x_fn }, - - { SPCI_FL_BASE0, 4, 921600, /* IOMEM */ /* pbn_computone_4 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 6, 921600, /* IOMEM */ /* pbn_computone_6 */ - 0x40, 2, NULL, 0x200 }, - { SPCI_FL_BASE0, 8, 921600, /* IOMEM */ /* pbn_computone_8 */ - 0x40, 2, NULL, 0x200 }, + [pbn_nec_nile4] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 520833, + .uart_offset = 8 << 3, + .reg_shift = 3, + .first_offset = 0x300, + }, + + /* + * Computone - uses IOMEM. + */ + [pbn_computone_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_6] = { + .flags = FL_BASE0, + .num_ports = 6, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, + [pbn_computone_8] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 0x40, + .reg_shift = 2, + .first_offset = 0x200, + }, }; /* @@ -640,8 +1363,7 @@ static struct pci_board pci_boards[] __devinitdata = { static int __devinit serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) { - int num_iomem = 0, num_port = 0, first_port = -1; - int i; + int num_iomem, num_port, first_port = -1, i; /* * If it is not a communications device or the programming @@ -655,36 +1377,63 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) (dev->class & 0xff) > 6) return -ENODEV; - for (i = 0; i < 6; i++) { - if (IS_PCI_REGION_IOPORT(dev, i)) { + num_iomem = num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO) { num_port++; if (first_port == -1) first_port = i; } - if (IS_PCI_REGION_IOMEM(dev, i)) + if (pci_resource_flags(dev, i) & IORESOURCE_MEM) num_iomem++; } /* - * If there is 1 or 0 iomem regions, and exactly one port, use - * it. + * If there is 1 or 0 iomem regions, and exactly one port, + * use it. We guess the number of ports based on the IO + * region size. */ if (num_iomem <= 1 && num_port == 1) { board->flags = first_port; + board->num_ports = pci_resource_len(dev, first_port) / 8; + return 0; + } + + /* + * Now guess if we've got a board which indexes by BARs. + * Each IO BAR should be 8 bytes, and they should follow + * consecutively. + */ + first_port = -1; + num_port = 0; + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (pci_resource_flags(dev, i) & IORESOURCE_IO && + pci_resource_len(dev, i) == 8 && + (first_port == -1 || (first_port + num_port) == i)) { + num_port++; + if (first_port == -1) + first_port = i; + } + } + + if (num_port > 1) { + board->flags = first_port | FL_BASE_BARS; + board->num_ports = num_port; return 0; } + return -ENODEV; } static inline int -serial_pci_matches(struct pci_board *board, int index) +serial_pci_matches(struct pci_board *board, struct pci_board *guessed) { 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; + board->num_ports == guessed->num_ports && + board->base_baud == guessed->base_baud && + board->uart_offset == guessed->uart_offset && + board->reg_shift == guessed->reg_shift && + board->first_offset == guessed->first_offset; } /* @@ -692,10 +1441,11 @@ serial_pci_matches(struct pci_board *board, int index) * to the arrangement of serial ports on a PCI card. */ static int __devinit -pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +pciserial_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { struct serial_private *priv; struct pci_board *board, tmp; + struct pci_serial_quirk *quirk; struct serial_struct serial_req; int base_baud, rc, nr_ports, i; @@ -732,92 +1482,109 @@ pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) * detect this boards settings with our heuristic, * then we no longer need this entry. */ + memcpy(&tmp, &pci_boards[pbn_default], sizeof(struct pci_board)); 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, - pci_get_subvendor(dev), pci_get_subdevice(dev)); - } + if (rc == 0 && serial_pci_matches(board, &tmp)) + moan_device("Redundant entry in serial pci_table.", + dev); } nr_ports = board->num_ports; /* - * Run the initialization function, if any. The initialization - * function returns: + * Find an init and setup quirks. + */ + quirk = find_quirk(dev); + + /* + * Run the new-style initialization function. + * The initialization function returns: * <0 - error * 0 - use board->num_ports * >0 - number of ports */ - if (board->init_fn) { - rc = board->init_fn(dev, 1); + if (quirk->init) { + rc = quirk->init(dev); if (rc < 0) goto disable; - if (rc) nr_ports = rc; } priv = kmalloc(sizeof(struct serial_private) + - sizeof(unsigned int) * nr_ports, - GFP_KERNEL); + sizeof(unsigned int) * nr_ports, + GFP_KERNEL); if (!priv) { rc = -ENOMEM; goto deinit; } + memset(priv, 0, sizeof(struct serial_private) + + sizeof(unsigned int) * nr_ports); + + priv->quirk = quirk; + pci_set_drvdata(dev, priv); + base_baud = board->base_baud; - if (!base_baud) + if (!base_baud) { + moan_device("Board entry does not specify baud rate.", dev); base_baud = BASE_BAUD; - memset(&serial_req, 0, sizeof(serial_req)); + } for (i = 0; i < nr_ports; i++) { + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.flags = UPF_SKIP_TEST | UPF_AUTOPROBE | + UPF_RESOURCES | UPF_SHARE_IRQ; + serial_req.baud_base = base_baud; serial_req.irq = get_pci_irq(dev, board, i); - if (get_pci_port(dev, board, &serial_req, i)) + if (quirk->setup(dev, board, &serial_req, i)) break; #ifdef SERIAL_DEBUG_PCI printk("Setup PCI port: port %x, irq %d, type %d\n", serial_req.port, serial_req.irq, serial_req.io_type); #endif - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; - serial_req.baud_base = base_baud; priv->line[i] = register_serial(&serial_req); if (priv->line[i] < 0) break; } - priv->board = board; priv->nr = i; - pci_set_drvdata(dev, priv); - return 0; deinit: - if (board->init_fn) - board->init_fn(dev, 0); + if (quirk->exit) + quirk->exit(dev); disable: pci_disable_device(dev); return rc; } -static void __devexit pci_remove_one(struct pci_dev *dev) +static void __devexit pciserial_remove_one(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); - int i; pci_set_drvdata(dev, NULL); if (priv) { + struct pci_serial_quirk *quirk; + int i; + for (i = 0; i < priv->nr; i++) unregister_serial(priv->line[i]); - if (priv->board->init_fn) - priv->board->init_fn(dev, 0); + for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++) { + if (priv->remapped_bar[i]) + iounmap(priv->remapped_bar[i]); + priv->remapped_bar[i] = NULL; + } + + /* + * Find the exit quirks. + */ + quirk = find_quirk(dev); + if (quirk->exit) + quirk->exit(dev); pci_disable_device(dev); @@ -908,7 +1675,9 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_921600 }, - /* VScom SPCOM800, from sl@s.pl */ + /* + * VScom SPCOM800, from sl@s.pl + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_SPCOM800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_8_921600 }, @@ -949,8 +1718,10 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_SUBVENDOR_ID_CHASE_PCIRAS, PCI_SUBDEVICE_ID_CHASE_PCIRAS8, 0, 0, pbn_b2_8_460800 }, - /* Megawolf Romulus PCI Serial Card, from Mike Hudson */ - /* (Exoray@isys.ca) */ + /* + * Megawolf Romulus PCI Serial Card, from Mike Hudson + * (Exoray@isys.ca) + */ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_ROMULUS, 0x10b5, 0x106a, 0, 0, pbn_plx_romulus }, @@ -976,16 +1747,24 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_2_115200 }, - /* Digitan DS560-558, from jimd@esoft.com */ + /* + * Digitan DS560-558, from jimd@esoft.com + */ { PCI_VENDOR_ID_ATT, PCI_DEVICE_ID_ATT_VENUS_MODEM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b1_1_115200 }, - /* 3Com US Robotics 56k Voice Internal PCI model 5610 */ + /* + * 3Com US Robotics 56k Voice Internal PCI model 5610 + */ { PCI_VENDOR_ID_USR, 0x1008, - PCI_ANY_ID, PCI_ANY_ID, }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, - /* Titan Electronic cards */ + /* + * Titan Electronic cards + * The 400L and 800L have a custom setup quirk. + */ { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_921600 }, @@ -999,120 +1778,76 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1, 1, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_1_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, - /* The 400L and 800L have a custom hack in get_pci_port */ + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b1_bt_2_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 4, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, - PCI_ANY_ID, PCI_ANY_ID, - SPCI_FL_BASE_TABLE, 8, 921600 }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_8_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_1 }, + pbn_b2_1_460800 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_2 }, + pbn_b2_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_10x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig10x_4 }, + pbn_b2_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_0 }, + pbn_b0_1_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_2 }, + pbn_b0_bt_2_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_4S_20x_850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_siig20x_4 }, + pbn_b0_bt_4_921600 }, - /* Computone devices submitted by Doug McNash dmcnash@computone.com */ + /* + * Computone devices submitted by Doug McNash dmcnash@computone.com + */ { PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_PG, PCI_SUBVENDOR_ID_COMPUTONE, PCI_SUBDEVICE_ID_COMPUTONE_PG4, 0, 0, pbn_computone_4 }, @@ -1124,18 +1859,22 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0, 0, pbn_computone_6 }, { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI95N, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_oxsemi }, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi }, { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, - PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, pbn_timedia }, + PCI_VENDOR_ID_TIMEDIA, PCI_ANY_ID, 0, 0, + pbn_b0_bt_1_921600 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - /* AFAVLAB serial card, from Harald Welte */ + /* + * AFAVLAB serial card, from Harald Welte + */ { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_P028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_8_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_DSERIAL, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, @@ -1158,26 +1897,40 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_1_460800 }, - /* RAStel 2 port modem, gerg@moreton.com.au */ + /* + * RAStel 2 port modem, gerg@moreton.com.au + */ { PCI_VENDOR_ID_MORETON, PCI_DEVICE_ID_RASTEL_2PORT, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_bt_2_115200 }, - /* EKF addition for i960 Boards form EKF with serial port */ - { PCI_VENDOR_ID_INTEL, 0x1960, + /* + * EKF addition for i960 Boards form EKF with serial port + */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80960_RP, 0xE4BF, PCI_ANY_ID, 0, 0, pbn_intel_i960 }, - /* Xircom Cardbus/Ethernet combos */ + /* + * Xircom Cardbus/Ethernet combos + */ { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_xircom_combo }, + pbn_b0_1_115200 }, + /* + * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) + */ + { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_1_115200 }, /* * Untested PCI modems, sent in from various folks... */ - /* Elsa Model 56K PCI Modem, from Andreas Rath */ + /* + * Elsa Model 56K PCI Modem, from Andreas Rath + */ { PCI_VENDOR_ID_ROCKWELL, 0x1004, 0x1048, 0x1500, 0, 0, pbn_b1_1_115200 }, @@ -1186,10 +1939,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { 0xFF00, 0, 0, 0, pbn_sgi_ioc3 }, - /* HP Diva card */ + /* + * HP Diva card + */ { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_hp_diva }, + pbn_b0_5_115200 }, { PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_DIVA_AUX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b2_1_115200 }, @@ -1203,15 +1958,14 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom4 }, + pbn_b3_4_115200 }, { PCI_VENDOR_ID_DCI, PCI_DEVICE_ID_DCI_PCCOM8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_dci_pccom8 }, + pbn_b3_8_115200 }, /* - * These entries match devices with class - * COMMUNICATION_SERIAL, COMMUNICATION_MODEM - * or COMMUNICATION_MULTISERIAL + * 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, @@ -1230,9 +1984,12 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { static struct pci_driver serial_pci_driver = { .name = "serial", - .probe = pci_init_one, - .remove = __devexit_p(pci_remove_one), + .probe = pciserial_init_one, + .remove = __devexit_p(pciserial_remove_one), .id_table = serial_pci_tbl, + .driver = { + .devclass = &tty_devclass, + }, }; static int __init serial8250_pci_init(void) -- cgit v1.2.3 From 842cd3cbcd8d2426900be893e13db0dc06ea4d19 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Mar 2003 19:26:09 +0000 Subject: [SERIAL] Update 8250_acorn.c Add resource-based addressing and ioremap support. Ask the 8250 layer to handle the resources, and share IRQs. --- drivers/serial/8250_acorn.c | 76 ++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c index 0065a31a418a..db4666f894e5 100644 --- a/drivers/serial/8250_acorn.c +++ b/drivers/serial/8250_acorn.c @@ -1,7 +1,7 @@ /* * linux/drivers/serial/acorn.c * - * Copyright (C) 1996-2002 Russell King. + * Copyright (C) 1996-2003 Russell King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -9,6 +9,8 @@ */ #include #include +#include +#include #include #include #include @@ -16,6 +18,7 @@ #include #include +#include #include #include @@ -24,36 +27,41 @@ struct serial_card_type { unsigned int num_ports; unsigned int baud_base; - int type; - int speed; - int offset[MAX_PORTS]; + unsigned int type; + unsigned int offset[MAX_PORTS]; }; struct serial_card_info { unsigned int num_ports; int ports[MAX_PORTS]; - unsigned long base[MAX_PORTS]; }; -static inline int serial_register_onedev(unsigned long port, int irq, unsigned int baud_base) +static inline int +serial_register_onedev(unsigned long baddr, void *vaddr, int irq, unsigned int baud_base) { struct serial_struct req; memset(&req, 0, sizeof(req)); - req.baud_base = baud_base; - req.irq = irq; - req.port = port; - req.flags = 0; + req.irq = irq; + req.flags = UPF_AUTOPROBE | UPF_RESOURCES | + UPF_SHARE_IRQ; + req.baud_base = baud_base; + req.io_type = UPIO_MEM; + req.iomem_base = vaddr; + req.iomem_reg_shift = 2; + req.iomap_base = baddr; return register_serial(&req); } -static int __devinit serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) +static int __devinit +serial_card_probe(struct expansion_card *ec, const struct ecard_id *id) { struct serial_card_info *info; struct serial_card_type *type = id->data; - unsigned long cardaddr, address; - int port; + unsigned long bus_addr; + unsigned char *virt_addr; + unsigned int port; info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL); if (!info) @@ -64,20 +72,19 @@ static int __devinit serial_card_probe(struct expansion_card *ec, const struct e ecard_set_drvdata(ec, info); - cardaddr = ecard_address(ec, type->type, type->speed); + bus_addr = ec->resource[type->type].start; + virt_addr = ioremap(bus_addr, ec->resource[type->type].end - bus_addr + 1); + if (!virt_addr) { + kfree(info); + return -ENOMEM; + } for (port = 0; port < info->num_ports; port ++) { - address = cardaddr + type->offset[port]; - - info->ports[port] = -1; - info->base[port] = address; + unsigned long baddr = bus_addr + type->offset[port]; + unsigned char *vaddr = virt_addr + type->offset[port]; - if (!request_region(address, 8, "acornserial")) - continue; - - info->ports[port] = serial_register_onedev(address, ec->irq, type->baud_base); - if (info->ports[port] < 0) - break; + info->ports[port] = serial_register_onedev(baddr, vaddr, + ec->irq, type->baud_base); } return 0; @@ -90,12 +97,9 @@ static void __devexit serial_card_remove(struct expansion_card *ec) ecard_set_drvdata(ec, NULL); - for (i = 0; i < info->num_ports; i++) { - if (info->ports[i] > 0) { + for (i = 0; i < info->num_ports; i++) + if (info->ports[i] > 0) unregister_serial(info->ports[i]); - release_region(info->base[i], 8); - } - } kfree(info); } @@ -103,17 +107,15 @@ static void __devexit serial_card_remove(struct expansion_card *ec) static struct serial_card_type atomwide_type = { .num_ports = 3, .baud_base = 7372800 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0xa00, 0x900, 0x800 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2800, 0x2400, 0x2000 }, }; static struct serial_card_type serport_type = { .num_ports = 2, .baud_base = 3686400 / 16, - .type = ECARD_IOC, - .speed = ECARD_SLOW, - .offset = { 0x800, 0x808 }, + .type = ECARD_RES_IOCSLOW, + .offset = { 0x2000, 0x2020 }, }; static const struct ecard_id serial_cids[] = { @@ -127,7 +129,8 @@ static struct ecard_driver serial_card_driver = { .remove = __devexit_p(serial_card_remove), .id_table = serial_cids, .drv = { - .name = "acornserial", + .devclass = &tty_devclass, + .name = "8250_acorn", }, }; @@ -142,6 +145,7 @@ static void __exit serial_card_exit(void) } MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Acorn 8250-compatible serial port expansion card driver"); MODULE_LICENSE("GPL"); module_init(serial_card_init); -- cgit v1.2.3 From 28f23c1affddff07c538ed2886143a0a1e76d8c4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Mar 2003 20:51:16 +0000 Subject: [SERIAL] Add ttydriver->owner initialisation. --- drivers/serial/core.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 5b394b10cf2e..9cb1207c58cd 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -1288,7 +1288,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) wake_up_interruptible(&info->open_wait); done: - module_put(drv->owner); + ; } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) @@ -1571,29 +1571,14 @@ static int uart_open(struct tty_struct *tty, struct file *filp) if (line >= tty->driver.num) goto fail; - /* - * If we fail to increment the module use count, we can't have - * any other users of this tty (since this implies that the module - * is about to be unloaded). Therefore, it is safe to set - * tty->driver_data to be NULL, so uart_close() doesn't bite us. - */ - if (!try_module_get(drv->owner)) { - tty->driver_data = NULL; - goto fail; - } - /* * FIXME: This one isn't fun. We can't guarantee that the tty isn't * already in open, nor can we guarantee the state of tty->driver_data */ info = uart_get(drv, line); retval = -ENOMEM; - if (!info) { - if (tty->driver_data) - goto fail; - else - goto out; - } + if (!info) + goto fail; /* * Once we set tty->driver_data here, we are guaranteed that @@ -1656,8 +1641,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) return retval; - out: - module_put(drv->owner); fail: return retval; } @@ -2186,6 +2169,7 @@ int uart_register_driver(struct uart_driver *drv) drv->tty_driver = normal; normal->magic = TTY_DRIVER_MAGIC; + normal->owner = drv->owner; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; -- cgit v1.2.3 From 2641cc66eac1ad55f7dc731d80500de63efabf37 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Mar 2003 21:18:15 +0000 Subject: [SERIAL] Make tty->driver_data point at the uart_state structure. Since the uart_state structure is less volatile than uart_info, we can safely place some semaphores in uart_state. --- drivers/serial/core.c | 320 +++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 161 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 9cb1207c58cd..5812081297e7 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -73,8 +73,8 @@ void uart_write_wakeup(struct uart_port *port) static void uart_stop(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -84,24 +84,25 @@ static void uart_stop(struct tty_struct *tty) static void __uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; - if (!uart_circ_empty(&info->xmit) && info->xmit.buf && + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port, 1); } static void uart_start(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; - pm_access(info->state->pm); + pm_access(state->pm); - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); } static void uart_tasklet_action(unsigned long data) @@ -220,7 +221,7 @@ static void uart_shutdown(struct uart_info *info) * Turn off DTR and RTS early. */ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -526,10 +527,10 @@ __uart_kern_write(struct uart_port *port, struct circ_buf *circ, static void uart_put_char(struct tty_struct *tty, unsigned char ch) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (tty) - __uart_put_char(info->port, &info->xmit, ch); + __uart_put_char(state->port, &state->info->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -541,16 +542,16 @@ static int uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, int count) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; int ret; - if (!tty || !info->xmit.buf) + if (!tty || !state->info->xmit.buf) return 0; if (from_user) - ret = __uart_user_write(info->port, &info->xmit, buf, count); + ret = __uart_user_write(state->port, &state->info->xmit, buf, count); else - ret = __uart_kern_write(info->port, &info->xmit, buf, count); + ret = __uart_kern_write(state->port, &state->info->xmit, buf, count); uart_start(tty); return ret; @@ -558,29 +559,30 @@ uart_write(struct tty_struct *tty, int from_user, const unsigned char * buf, static int uart_write_room(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_free(&info->xmit); + return uart_circ_chars_free(&state->info->xmit); } static int uart_chars_in_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; - return uart_circ_chars_pending(&info->xmit); + return uart_circ_chars_pending(&state->info->xmit); } static void uart_flush_buffer(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; DPRINTK("uart_flush_buffer(%d) called\n", minor(tty->device) - tty->driver.minor_start); - spin_lock_irqsave(&info->port->lock, flags); - uart_circ_clear(&info->xmit); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -593,8 +595,8 @@ static void uart_flush_buffer(struct tty_struct *tty) */ static void uart_send_xchar(struct tty_struct *tty, char ch) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) @@ -611,19 +613,19 @@ static void uart_send_xchar(struct tty_struct *tty, char ch) static void uart_throttle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; if (I_IXOFF(tty)) uart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(info->port, TIOCM_RTS); + uart_clear_mctrl(state->port, TIOCM_RTS); } static void uart_unthrottle(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; if (I_IXOFF(tty)) { if (port->x_char) @@ -636,10 +638,9 @@ static void uart_unthrottle(struct tty_struct *tty) uart_set_mctrl(port, TIOCM_RTS); } -static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) +static int uart_get_info(struct uart_state *state, struct serial_struct *retinfo) { - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); @@ -649,7 +650,7 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) if (HIGH_BITS_OFFSET) tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; tmp.irq = port->irq; - tmp.flags = port->flags | info->flags; + tmp.flags = port->flags; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay; @@ -666,11 +667,10 @@ static int uart_get_info(struct uart_info *info, struct serial_struct *retinfo) } static int -uart_set_info(struct uart_info *info, struct serial_struct *newinfo) +uart_set_info(struct uart_state *state, struct serial_struct *newinfo) { struct serial_struct new_serial; - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned long new_port; unsigned int change_irq, change_port, old_flags; unsigned int old_custom_divisor; @@ -745,14 +745,14 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) /* * Make sure that we are the sole user of this port. */ - if (state->count > 1 || info->blocked_open != 0) + if (state->count > 1 || state->info->blocked_open != 0) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(info); + uart_shutdown(state->info); } if (change_port) { @@ -818,13 +818,15 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; port->fifosize = new_serial.xmit_fifo_size; - info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + if (state->info && state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; if (port->type == PORT_UNKNOWN) goto exit; - if (info->flags & UIF_INITIALIZED) { + if (state->info && state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* If they're setting up a custom divisor or speed, @@ -832,13 +834,13 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (port->flags & UPF_SPD_MASK) { printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", - current->comm, info->tty->driver.name, - info->port->line); + current->comm, state->info->tty->driver.name, + state->port->line); } - uart_change_speed(info, NULL); + uart_change_speed(state->info, NULL); } } else - retval = uart_startup(info, 1); + retval = uart_startup(state->info, 1); exit: up(&port_sem); return retval; @@ -848,9 +850,9 @@ uart_set_info(struct uart_info *info, struct serial_struct *newinfo) /* * uart_get_lsr_info - get line status register info */ -static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) +static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; unsigned int result; result = port->ops->tx_empty(port); @@ -861,9 +863,9 @@ static int uart_get_lsr_info(struct uart_info *info, unsigned int *value) * avoid a race condition (depending on when the transmit * interrupt happens). */ - if (info->port->x_char || - ((uart_circ_chars_pending(&info->xmit) > 0) && - !info->tty->stopped && !info->tty->hw_stopped)) + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -911,8 +913,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, static void uart_break_ctl(struct tty_struct *tty, int break_state) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; BUG_ON(!kernel_locked()); @@ -920,9 +922,9 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state) port->ops->break_ctl(port, break_state); } -static int uart_do_autoconfig(struct uart_info *info) +static int uart_do_autoconfig(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) @@ -937,8 +939,8 @@ static int uart_do_autoconfig(struct uart_info *info) return -ERESTARTSYS; ret = -EBUSY; - if (info->state->count == 1 && info->blocked_open == 0) { - uart_shutdown(info); + if (state->count == 1 && state->info->blocked_open == 0) { + uart_shutdown(state->info); /* * If we already have a port type configured, @@ -957,16 +959,16 @@ static int uart_do_autoconfig(struct uart_info *info) */ port->ops->config_port(port, flags); - ret = uart_startup(info, 1); + ret = uart_startup(state->info, 1); } up(&port_sem); return ret; } static int -uart_wait_modem_status(struct uart_info *info, unsigned long arg) +uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *port = info->port; + struct uart_port *port = state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; @@ -983,7 +985,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) port->ops->enable_ms(port); spin_unlock_irq(&port->lock); - add_wait_queue(&info->delta_msr_wait, &wait); + add_wait_queue(&state->info->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&port->lock); memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); @@ -1011,7 +1013,7 @@ uart_wait_modem_status(struct uart_info *info, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&info->delta_msr_wait, &wait); + remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret; } @@ -1023,7 +1025,7 @@ static int uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; struct serial_icounter_struct icount; struct uart_icount cnow; int ret = -ENOIOCTLCMD; @@ -1039,31 +1041,31 @@ uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, switch (cmd) { case TIOCMGET: - ret = uart_get_modem_info(info->port, + ret = uart_get_modem_info(state->port, (unsigned int *)arg); break; case TIOCMBIS: case TIOCMBIC: case TIOCMSET: - ret = uart_set_modem_info(info->port, cmd, + ret = uart_set_modem_info(state->port, cmd, (unsigned int *)arg); break; case TIOCGSERIAL: - ret = uart_get_info(info, (struct serial_struct *)arg); + ret = uart_get_info(state, (struct serial_struct *)arg); break; case TIOCSSERIAL: - ret = uart_set_info(info, (struct serial_struct *)arg); + ret = uart_set_info(state, (struct serial_struct *)arg); break; case TIOCSERCONFIG: - ret = uart_do_autoconfig(info); + ret = uart_do_autoconfig(state); break; case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(info, (unsigned int *)arg); + ret = uart_get_lsr_info(state, (unsigned int *)arg); break; /* @@ -1073,7 +1075,7 @@ uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: - ret = uart_wait_modem_status(info, arg); + ret = uart_wait_modem_status(state, arg); break; /* @@ -1083,10 +1085,10 @@ uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, * RI where only 0->1 is counted. */ case TIOCGICOUNT: - spin_lock_irq(&info->port->lock); - memcpy(&cnow, &info->port->icount, + spin_lock_irq(&state->port->lock); + memcpy(&cnow, &state->port->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&info->port->lock); + spin_unlock_irq(&state->port->lock); icount.cts = cnow.cts; icount.dsr = cnow.dsr; @@ -1110,7 +1112,7 @@ uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, break; default: { - struct uart_port *port = info->port; + struct uart_port *port = state->port; if (port->ops->ioctl) ret = port->ops->ioctl(port, cmd, arg); break; @@ -1121,7 +1123,7 @@ uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios) { - struct uart_info *info = tty->driver_data; + struct uart_state *state = tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; @@ -1137,11 +1139,11 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; - uart_change_speed(info, old_termios); + uart_change_speed(state->info, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(info->port, TIOCM_RTS | TIOCM_DTR); + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { @@ -1149,15 +1151,15 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; - uart_set_mctrl(info->port, mask); + uart_set_mctrl(state->port, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&state->port->lock, flags); tty->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&state->port->lock, flags); } #if 0 @@ -1169,7 +1171,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&state->info->open_wait); #endif } @@ -1180,19 +1182,15 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios */ static void uart_close(struct tty_struct *tty, struct file *filp) { - struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; - struct uart_state *state; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long flags; BUG_ON(!kernel_locked()); - if (!info) + if (!state->info) return; - state = info->state; - DPRINTK("uart_close() called\n"); /* @@ -1202,7 +1200,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) if (tty_hung_up_p(filp)) goto done; - spin_lock_irqsave(&info->port->lock, flags); + spin_lock_irqsave(&state->port->lock, flags); if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty @@ -1217,11 +1215,11 @@ static void uart_close(struct tty_struct *tty, struct file *filp) } if (--state->count < 0) { printk("rs_close: bad serial port count for %s%d: %d\n", - tty->driver.name, info->port->line, state->count); + tty->driver.name, port->line, state->count); state->count = 0; } if (state->count) { - spin_unlock_irqrestore(&info->port->lock, flags); + spin_unlock_irqrestore(&state->port->lock, flags); goto done; } @@ -1229,22 +1227,22 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * The UIF_CLOSING flag protects us against further opens * of this port. */ - info->flags |= UIF_CLOSING; - spin_unlock_irqrestore(&info->port->lock, flags); + state->info->flags |= UIF_CLOSING; + spin_unlock_irqrestore(&state->port->lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; - if (info->state->closing_wait != USF_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->state->closing_wait); + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -1256,17 +1254,17 @@ static void uart_close(struct tty_struct *tty, struct file *filp) uart_wait_until_sent(tty, port->timeout); } down(&port_sem); - uart_shutdown(info); + uart_shutdown(state->info); up(&port_sem); uart_flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; - info->tty = NULL; - if (info->blocked_open) { - if (info->state->close_delay) { + state->info->tty = NULL; + if (state->info->blocked_open) { + if (state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(info->state->close_delay); + schedule_timeout(state->close_delay); set_current_state(TASK_RUNNING); } } else { @@ -1274,7 +1272,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) /* * Put device into D3 state. */ - pm_send(info->state->pm, PM_SUSPEND, (void *)3); + pm_send(state->pm, PM_SUSPEND, (void *)3); #else if (port->ops->pm) port->ops->pm(port, 3, 0); @@ -1284,8 +1282,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) /* * Wake up anyone trying to open this port. */ - info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); - wake_up_interruptible(&info->open_wait); + state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); + wake_up_interruptible(&state->info->open_wait); done: ; @@ -1293,8 +1291,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp) static void uart_wait_until_sent(struct tty_struct *tty, int timeout) { - struct uart_info *info = tty->driver_data; - struct uart_port *port = info->port; + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->port; unsigned long char_time, expire; BUG_ON(!kernel_locked()); @@ -1358,23 +1356,22 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) */ static void uart_hangup(struct tty_struct *tty) { - struct uart_info *info = tty->driver_data; - struct uart_state *state = info->state; + struct uart_state *state = tty->driver_data; BUG_ON(!kernel_locked()); uart_flush_buffer(tty); down(&port_sem); - if (info->flags & UIF_CLOSING) { + if (state->info->flags & UIF_CLOSING) { up(&port_sem); return; } - uart_shutdown(info); + uart_shutdown(state->info); state->count = 0; - info->flags &= ~UIF_NORMAL_ACTIVE; - info->tty = NULL; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; up(&port_sem); - wake_up_interruptible(&info->open_wait); + wake_up_interruptible(&state->info->open_wait); } /* @@ -1416,11 +1413,11 @@ static void uart_update_termios(struct uart_info *info) } static int -uart_block_til_ready(struct file *filp, struct uart_info *info) +uart_block_til_ready(struct file *filp, struct uart_state *state) { DECLARE_WAITQUEUE(wait, current); - struct uart_state *state = info->state; - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; info->blocked_open++; state->count--; @@ -1470,7 +1467,7 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) * the data from the modem. */ if (info->tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR); + uart_set_mctrl(port, TIOCM_DTR); /* * and wait for the carrier to indicate that the @@ -1504,42 +1501,44 @@ uart_block_til_ready(struct file *filp, struct uart_info *info) return 0; } -static struct uart_info *uart_get(struct uart_driver *drv, int line) +static struct uart_state *uart_get(struct uart_driver *drv, int line) { - struct uart_state *state = drv->state + line; - struct uart_info *info = NULL; + struct uart_state *state; down(&port_sem); - if (!state->port) - goto out; - + state = drv->state + line; state->count++; - info = state->info; + if (!state->port) { + up(&port_sem); + state->count--; + return ERR_PTR(-ENXIO); + } - if (!info) { - info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); - if (info) { - memset(info, 0, sizeof(struct uart_info)); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->delta_msr_wait); + if (!state->info) { + state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); /* * Link the info into the other structures. */ - info->port = state->port; - info->state = state; - state->port->info = info; - - tasklet_init(&info->tlet, uart_tasklet_action, - (unsigned long)info); - state->info = info; - } else + state->port->info = state->info; + state->info->port = state->port; + state->info->state = state; + + tasklet_init(&state->info->tlet, uart_tasklet_action, + (unsigned long)state->info); + up(&port_sem); + } else { state->count--; + up(&port_sem); + return ERR_PTR(-ENOMEM); + } } - out: - up(&port_sem); - return info; + return state; } /* @@ -1555,7 +1554,7 @@ static struct uart_info *uart_get(struct uart_driver *drv, int line) static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; - struct uart_info *info; + struct uart_state *state; int retval, line = minor(tty->device) - tty->driver.minor_start; BUG_ON(!kernel_locked()); @@ -1575,28 +1574,29 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * FIXME: This one isn't fun. We can't guarantee that the tty isn't * already in open, nor can we guarantee the state of tty->driver_data */ - info = uart_get(drv, line); - retval = -ENOMEM; - if (!info) + state = uart_get(drv, line); + if (IS_ERR(state)) { + retval = PTR_ERR(state); goto fail; + } /* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */ - tty->driver_data = info; - tty->low_latency = (info->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - info->tty = tty; + state->info->tty = tty; /* * If the port is in the middle of closing, bail out now. */ - if (tty_hung_up_p(filp) || (info->flags & UIF_CLOSING)) { - wait_event_interruptible(info->open_wait, - !(info->flags & UIF_CLOSING)); - retval = (info->port->flags & UPF_HUP_NOTIFY) ? + if (tty_hung_up_p(filp) || (state->info->flags & UIF_CLOSING)) { + wait_event_interruptible(state->info->open_wait, + !(state->info->flags & UIF_CLOSING)); + retval = (state->port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; goto fail; } @@ -1604,11 +1604,11 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * Make sure the device is in D0 state. */ - if (info->state->count == 1) { + if (state->count == 1) { #ifdef CONFIG_PM - pm_send(info->state->pm, PM_RESUME, (void *)0); + pm_send(state->pm, PM_RESUME, (void *)0); #else - struct uart_port *port = info->port; + struct uart_port *port = state->port; if (port->ops->pm) port->ops->pm(port, 0, 3); #endif @@ -1620,7 +1620,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * we sleep while requesting an IRQ. */ down(&port_sem); - retval = uart_startup(info, 0); + retval = uart_startup(state->info, 0); up(&port_sem); if (retval) goto fail; @@ -1628,19 +1628,17 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * Wait until the port is ready. */ - retval = uart_block_til_ready(filp, info); + retval = uart_block_til_ready(filp, state); /* * If this is the first open to succeed, adjust things to suit. */ - if (retval == 0 && !(info->flags & UIF_NORMAL_ACTIVE)) { - info->flags |= UIF_NORMAL_ACTIVE; + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; - uart_update_termios(info); + uart_update_termios(state->info); } - return retval; - fail: return retval; } -- cgit v1.2.3 From 4c34d07e49b3e2ea01b70149e834e3dc721c7906 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 8 Mar 2003 21:23:36 +0000 Subject: [SERIAL] Make uart_tasklet_action take uart_state --- drivers/serial/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 5812081297e7..650181c47159 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -107,10 +107,10 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { - struct uart_info *info = (struct uart_info *)data; + struct uart_state *state = (struct uart_state *)data; struct tty_struct *tty; - tty = info->tty; + tty = state->info->tty; if (tty) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) @@ -1529,7 +1529,7 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) state->info->state = state; tasklet_init(&state->info->tlet, uart_tasklet_action, - (unsigned long)state->info); + (unsigned long)state); up(&port_sem); } else { state->count--; -- cgit v1.2.3 From 1208e2cb97bcf3492cd37ae3b048046abecba150 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Mar 2003 00:39:38 +0000 Subject: [SERIAL] Eliminate some more passing of struct uart_info. Replace uart_info argument for uart_change_speed, uart_startup, uart_shutdown and uart_update_termios with a uart_state structure. We no longer pass struct uart_info around to other functions. --- drivers/serial/core.c | 56 ++++++++++++++++++++++----------------------- include/linux/serial_core.h | 2 -- 2 files changed, 28 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 650181c47159..2361d96c1260 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -58,7 +58,7 @@ static DECLARE_MUTEX(port_sem); #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -static void uart_change_speed(struct uart_info *info, struct termios *old_termios); +static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); /* @@ -140,9 +140,10 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) * Startup the port. This will be called once per open. All calls * will be serialised by the global port semaphore. */ -static int uart_startup(struct uart_info *info, int init_hw) +static int uart_startup(struct uart_state *state, int init_hw) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; unsigned long page; int retval = 0; @@ -183,7 +184,7 @@ static int uart_startup(struct uart_info *info, int init_hw) /* * Initialise the hardware port settings. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * Setup the RTS and DTR signals once the @@ -210,9 +211,10 @@ static int uart_startup(struct uart_info *info, int init_hw) * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by port_sem. */ -static void uart_shutdown(struct uart_info *info) +static void uart_shutdown(struct uart_state *state) { - struct uart_port *port = info->port; + struct uart_info *info = state->info; + struct uart_port *port = state->port; if (!(info->flags & UIF_INITIALIZED)) return; @@ -413,10 +415,10 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); static void -uart_change_speed(struct uart_info *info, struct termios *old_termios) +uart_change_speed(struct uart_state *state, struct termios *old_termios) { - struct tty_struct *tty = info->tty; - struct uart_port *port = info->port; + struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; struct termios *termios; /* @@ -432,14 +434,14 @@ uart_change_speed(struct uart_info *info, struct termios *old_termios) * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) - info->flags |= UIF_CTS_FLOW; + state->info->flags |= UIF_CTS_FLOW; else - info->flags &= ~UIF_CTS_FLOW; + state->info->flags &= ~UIF_CTS_FLOW; if (termios->c_cflag & CLOCAL) - info->flags &= ~UIF_CHECK_CD; + state->info->flags &= ~UIF_CHECK_CD; else - info->flags |= UIF_CHECK_CD; + state->info->flags |= UIF_CHECK_CD; port->ops->set_termios(port, termios, old_termios); } @@ -837,10 +839,10 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) current->comm, state->info->tty->driver.name, state->port->line); } - uart_change_speed(state->info, NULL); + uart_change_speed(state, NULL); } } else - retval = uart_startup(state->info, 1); + retval = uart_startup(state, 1); exit: up(&port_sem); return retval; @@ -959,7 +961,7 @@ static int uart_do_autoconfig(struct uart_state *state) */ port->ops->config_port(port, flags); - ret = uart_startup(state->info, 1); + ret = uart_startup(state, 1); } up(&port_sem); return ret; @@ -1139,7 +1141,7 @@ static void uart_set_termios(struct tty_struct *tty, struct termios *old_termios RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) return; - uart_change_speed(state->info, old_termios); + uart_change_speed(state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) @@ -1380,14 +1382,14 @@ static void uart_hangup(struct tty_struct *tty) * kernel settings, and the settings init adopts when it opens the port * for the first time. */ -static void uart_update_termios(struct uart_info *info) +static void uart_update_termios(struct uart_state *state) { - struct tty_struct *tty = info->tty; + struct tty_struct *tty = state->info->tty; #ifdef CONFIG_SERIAL_CORE_CONSOLE - struct console *c = info->port->cons; + struct console *c = state->port->cons; - if (c && c->cflag && c->index == info->port->line) { + if (c && c->cflag && c->index == state->port->line) { tty->termios->c_cflag = c->cflag; c->cflag = 0; } @@ -1402,13 +1404,13 @@ static void uart_update_termios(struct uart_info *info) /* * Make termios settings take effect. */ - uart_change_speed(info, NULL); + uart_change_speed(state, NULL); /* * And finally enable the RTS and DTR signals. */ if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(info->port, TIOCM_DTR | TIOCM_RTS); + uart_set_mctrl(state->port, TIOCM_DTR | TIOCM_RTS); } } @@ -1525,8 +1527,6 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) * Link the info into the other structures. */ state->port->info = state->info; - state->info->port = state->port; - state->info->state = state; tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); @@ -1620,7 +1620,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * we sleep while requesting an IRQ. */ down(&port_sem); - retval = uart_startup(state->info, 0); + retval = uart_startup(state, 0); up(&port_sem); if (retval) goto fail; @@ -1636,7 +1636,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp) if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { state->info->flags |= UIF_NORMAL_ACTIVE; - uart_update_termios(state->info); + uart_update_termios(state); } fail: @@ -1913,7 +1913,7 @@ static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstat */ ops->set_mctrl(port, 0); ops->startup(port); - uart_change_speed(state->info, NULL); + uart_change_speed(state, NULL); spin_lock_irq(&port->lock); ops->set_mctrl(port, port->mctrl); ops->start_tx(port, 0); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3412f8b7f2ce..0ea3262a03b6 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -224,8 +224,6 @@ struct uart_state { * stuff here. */ struct uart_info { - struct uart_port *port; - struct uart_state *state; struct tty_struct *tty; struct circ_buf xmit; unsigned int flags; -- cgit v1.2.3 From d9a6bc6ef013bb6bad03b760fb00219b72a5f8a7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Mar 2003 14:31:24 +0000 Subject: [SERIAL] Add per-port semaphore. Add a per-port semaphore to protect against simultaneous opens, closes, hangups, and the like. This removes the need for the UIF_CLOSING flag, as well as the extra tests and wait queues. Disable the old PM code for now - it is incompatible with the per-port semaphore. --- drivers/serial/core.c | 402 ++++++++++++++++++++++---------------------- include/linux/serial_core.h | 3 +- 2 files changed, 201 insertions(+), 204 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 2361d96c1260..54854cb06321 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -21,9 +21,6 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * $Id: core.c,v 1.100 2002/07/28 10:03:28 rmk Exp $ - * */ #include #include @@ -58,6 +55,8 @@ static DECLARE_MUTEX(port_sem); #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); @@ -138,7 +137,7 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) /* * Startup the port. This will be called once per open. All calls - * will be serialised by the global port semaphore. + * will be serialised by the per-port semaphore. */ static int uart_startup(struct uart_state *state, int init_hw) { @@ -196,8 +195,7 @@ static int uart_startup(struct uart_state *state, int init_hw) info->flags |= UIF_INITIALIZED; - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); + clear_bit(TTY_IO_ERROR, &info->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -209,7 +207,7 @@ static int uart_startup(struct uart_state *state, int init_hw) /* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to - * uart_shutdown are serialised by port_sem. + * uart_shutdown are serialised by the per-port semaphore. */ static void uart_shutdown(struct uart_state *state) { @@ -694,7 +692,7 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) * module insertion/removal doesn't change anything * under us. */ - down(&port_sem); + down(&state->sem); change_irq = new_serial.irq != port->irq; @@ -747,14 +745,14 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) /* * Make sure that we are the sole user of this port. */ - if (state->count > 1 || state->info->blocked_open != 0) + if (uart_users(state) > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(state->info); + uart_shutdown(state); } if (change_port) { @@ -820,7 +818,7 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; port->fifosize = new_serial.xmit_fifo_size; - if (state->info && state->info->tty) + if (state->info->tty) state->info->tty->low_latency = (port->flags & UPF_LOW_LATENCY) ? 1 : 0; @@ -828,7 +826,7 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) retval = 0; if (port->type == PORT_UNKNOWN) goto exit; - if (state->info && state->info->flags & UIF_INITIALIZED) { + if (state->info->flags & UIF_INITIALIZED) { if (((old_flags ^ port->flags) & UPF_SPD_MASK) || old_custom_divisor != port->custom_divisor) { /* If they're setting up a custom divisor or speed, @@ -844,13 +842,14 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) } else retval = uart_startup(state, 1); exit: - up(&port_sem); + up(&state->sem); return retval; } /* - * uart_get_lsr_info - get line status register info + * uart_get_lsr_info - get line status register info. + * Note: uart_ioctl protects us against hangups. */ static int uart_get_lsr_info(struct uart_state *state, unsigned int *value) { @@ -933,16 +932,16 @@ static int uart_do_autoconfig(struct uart_state *state) return -EPERM; /* - * Take the 'count' lock. This prevents count - * from incrementing, and hence any extra opens - * of the port while we're auto-configging. + * Take the per-port semaphore. This prevents count from + * changing, and hence any extra opens of the port while + * we're auto-configuring. */ - if (down_interruptible(&port_sem)) + if (down_interruptible(&state->sem)) return -ERESTARTSYS; ret = -EBUSY; - if (state->count == 1 && state->info->blocked_open == 0) { - uart_shutdown(state->info); + if (uart_users(state) == 1) { + uart_shutdown(state); /* * If we already have a port type configured, @@ -963,10 +962,16 @@ static int uart_do_autoconfig(struct uart_state *state) ret = uart_startup(state, 1); } - up(&port_sem); + up(&state->sem); return ret; } +/* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) { @@ -1020,106 +1025,133 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) return ret; } +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int +uart_get_count(struct uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount; + struct uart_icount cnow; + struct uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + /* * Called via sys_ioctl under the BKL. We can use spin_lock_irq() here. */ static int -uart_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, +uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) { struct uart_state *state = tty->driver_data; - struct serial_icounter_struct icount; - struct uart_icount cnow; int ret = -ENOIOCTLCMD; BUG_ON(!kernel_locked()); - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; + /* + * These ioctls don't rely on the hardware to be present. + */ + switch (cmd) { + case TIOCGSERIAL: + ret = uart_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = uart_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = uart_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; } - switch (cmd) { - case TIOCMGET: - ret = uart_get_modem_info(state->port, - (unsigned int *)arg); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - ret = uart_set_modem_info(state->port, cmd, - (unsigned int *)arg); - break; + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } - case TIOCGSERIAL: - ret = uart_get_info(state, (struct serial_struct *)arg); - break; + /* + * The following should only be used when hardware is present. + */ + switch (cmd) { + case TIOCMIWAIT: + ret = uart_wait_modem_status(state, arg); + break; - case TIOCSSERIAL: - ret = uart_set_info(state, (struct serial_struct *)arg); - break; + case TIOCGICOUNT: + ret = uart_get_count(state, (struct serial_icounter_struct *)arg); + break; + } - case TIOCSERCONFIG: - ret = uart_do_autoconfig(state); - break; + if (ret != -ENOIOCTLCMD) + goto out; - case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(state, (unsigned int *)arg); - break; + down(&state->sem); - /* - * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - * - mask passed in arg for lines of interest - * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) - * Caller should use TIOCGICOUNT to see which one it was - */ - case TIOCMIWAIT: - ret = uart_wait_modem_status(state, arg); - break; + if (tty_hung_up_p(filp)) { + ret = -EIO; + goto out_up; + } - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - spin_lock_irq(&state->port->lock); - memcpy(&cnow, &state->port->icount, - sizeof(struct uart_icount)); - spin_unlock_irq(&state->port->lock); - - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - ret = copy_to_user((void *)arg, &icount, sizeof(icount)) - ? -EFAULT : 0; - break; + /* + * All these rely on hardware being present and need to be + * protected against the tty being hung up. + */ + switch (cmd) { + case TIOCMGET: + ret = uart_get_modem_info(state->port, (unsigned int *)arg); + break; - case TIOCSERGWILD: /* obsolete */ - case TIOCSERSWILD: /* obsolete */ - ret = 0; - break; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + ret = uart_set_modem_info(state->port, cmd, + (unsigned int *)arg); + break; - default: { - struct uart_port *port = state->port; - if (port->ops->ioctl) - ret = port->ops->ioctl(port, cmd, arg); - break; - } + case TIOCSERGETLSR: /* Get line status register */ + ret = uart_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; } + } + out_up: + up(&state->sem); + out: return ret; } @@ -1186,23 +1218,15 @@ static void uart_close(struct tty_struct *tty, struct file *filp) { struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; - unsigned long flags; BUG_ON(!kernel_locked()); + DPRINTK("uart_close(%d) called\n", port->line); - if (!state->info) - return; - - DPRINTK("uart_close() called\n"); + down(&state->sem); - /* - * This is safe, as long as the BKL exists in - * do_tty_hangup(), and we're protected by the BKL. - */ if (tty_hung_up_p(filp)) goto done; - spin_lock_irqsave(&state->port->lock, flags); if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty @@ -1220,23 +1244,16 @@ static void uart_close(struct tty_struct *tty, struct file *filp) tty->driver.name, port->line, state->count); state->count = 0; } - if (state->count) { - spin_unlock_irqrestore(&state->port->lock, flags); + if (state->count) goto done; - } - - /* - * The UIF_CLOSING flag protects us against further opens - * of this port. - */ - state->info->flags |= UIF_CLOSING; - spin_unlock_irqrestore(&state->port->lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. + * the line discipline to only process XON/XOFF characters by + * setting tty->closing. */ tty->closing = 1; + if (state->closing_wait != USF_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, state->closing_wait); @@ -1245,6 +1262,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * disable the receive line status interrupts. */ if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); @@ -1255,14 +1273,14 @@ static void uart_close(struct tty_struct *tty, struct file *filp) */ uart_wait_until_sent(tty, port->timeout); } - down(&port_sem); - uart_shutdown(state->info); - up(&port_sem); + + uart_shutdown(state); uart_flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; state->info->tty = NULL; + if (state->info->blocked_open) { if (state->close_delay) { set_current_state(TASK_INTERRUPTIBLE); @@ -1270,25 +1288,18 @@ static void uart_close(struct tty_struct *tty, struct file *filp) set_current_state(TASK_RUNNING); } } else { -#ifdef CONFIG_PM - /* - * Put device into D3 state. - */ - pm_send(state->pm, PM_SUSPEND, (void *)3); -#else if (port->ops->pm) port->ops->pm(port, 3, 0); -#endif } /* * Wake up anyone trying to open this port. */ - state->info->flags &= ~(UIF_NORMAL_ACTIVE|UIF_CLOSING); + state->info->flags &= ~UIF_NORMAL_ACTIVE; wake_up_interruptible(&state->info->open_wait); done: - ; + up(&state->sem); } static void uart_wait_until_sent(struct tty_struct *tty, int timeout) @@ -1361,19 +1372,18 @@ static void uart_hangup(struct tty_struct *tty) struct uart_state *state = tty->driver_data; BUG_ON(!kernel_locked()); + DPRINTK("uart_hangup(%d)\n", state->port->line); - uart_flush_buffer(tty); - down(&port_sem); - if (state->info->flags & UIF_CLOSING) { - up(&port_sem); - return; + down(&state->sem); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + uart_flush_buffer(tty); + uart_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); } - uart_shutdown(state->info); - state->count = 0; - state->info->flags &= ~UIF_NORMAL_ACTIVE; - state->info->tty = NULL; - up(&port_sem); - wake_up_interruptible(&state->info->open_wait); + up(&state->sem); } /* @@ -1414,6 +1424,10 @@ static void uart_update_termios(struct uart_state *state) } } +/* + * Block the open until the port is ready. We must be called with + * the per-port semaphore held. + */ static int uart_block_til_ready(struct file *filp, struct uart_state *state) { @@ -1431,17 +1445,9 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) /* * If we have been hung up, tell userspace/restart open. */ - if (tty_hung_up_p(filp)) + if (tty_hung_up_p(filp) || info->tty == NULL) break; - /* - * If the device is in the middle of being closed, block - * until it's done. We will need to re-initialise the - * port. Hmm, is it legal to block a non-blocking open? - */ - if (info->flags & UIF_CLOSING) - goto wait; - /* * If the port has been closed, tell userspace/restart open. */ @@ -1478,8 +1484,9 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) if (port->ops->get_mctrl(port) & TIOCM_CAR) break; - wait: + up(&state->sem); schedule(); + down(&state->sem); if (signal_pending(current)) break; @@ -1493,10 +1500,7 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) if (signal_pending(current)) return -ERESTARTSYS; - if (info->tty->flags & (1 << TTY_IO_ERROR)) - return 0; - - if (tty_hung_up_p(filp) || !(info->flags & UIF_INITIALIZED)) + if (!info->tty || tty_hung_up_p(filp)) return (port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; @@ -1509,11 +1513,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) down(&port_sem); state = drv->state + line; + if (down_interruptible(&state->sem)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + state->count++; if (!state->port) { - up(&port_sem); state->count--; - return ERR_PTR(-ENXIO); + up(&state->sem); + state = ERR_PTR(-ENXIO); + goto out; } if (!state->info) { @@ -1530,14 +1540,15 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); - up(&port_sem); } else { state->count--; - up(&port_sem); - return ERR_PTR(-ENOMEM); + up(&state->sem); + state = ERR_PTR(-ENOMEM); } } + out: + up(&port_sem); return state; } @@ -1558,7 +1569,6 @@ static int uart_open(struct tty_struct *tty, struct file *filp) int retval, line = minor(tty->device) - tty->driver.minor_start; BUG_ON(!kernel_locked()); - DPRINTK("uart_open(%d) called\n", line); /* @@ -1571,8 +1581,11 @@ static int uart_open(struct tty_struct *tty, struct file *filp) goto fail; /* - * FIXME: This one isn't fun. We can't guarantee that the tty isn't - * already in open, nor can we guarantee the state of tty->driver_data + * We take the semaphore inside uart_get to guarantee that we won't + * be re-entered while allocating the info structure, or while we + * request any IRQs that the driver may need. This also has the nice + * side-effect that it delays the action of uart_hangup, so we can + * guarantee that info->tty will always contain something reasonable. */ state = uart_get(drv, line); if (IS_ERR(state)) { @@ -1593,11 +1606,11 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * If the port is in the middle of closing, bail out now. */ - if (tty_hung_up_p(filp) || (state->info->flags & UIF_CLOSING)) { - wait_event_interruptible(state->info->open_wait, - !(state->info->flags & UIF_CLOSING)); + if (tty_hung_up_p(filp)) { retval = (state->port->flags & UPF_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; + state->count--; + up(&state->sem); goto fail; } @@ -1605,30 +1618,22 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * Make sure the device is in D0 state. */ if (state->count == 1) { -#ifdef CONFIG_PM - pm_send(state->pm, PM_RESUME, (void *)0); -#else struct uart_port *port = state->port; if (port->ops->pm) port->ops->pm(port, 0, 3); -#endif } /* - * Start up the serial port. We have this semaphore here to - * prevent uart_startup or uart_shutdown being re-entered if - * we sleep while requesting an IRQ. + * Start up the serial port. */ - down(&port_sem); retval = uart_startup(state, 0); - up(&port_sem); - if (retval) - goto fail; /* - * Wait until the port is ready. + * If we succeeded, wait until the port is ready. */ - retval = uart_block_til_ready(filp, state); + if (retval == 0) + retval = uart_block_til_ready(filp, state); + up(&state->sem); /* * If this is the first open to succeed, adjust things to suit. @@ -1900,8 +1905,8 @@ static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstat port = state->port; ops = port->ops; - DPRINTK("pm: %08x: %d -> %d, %srunning\n", - port->iobase, dev->state, pm_state, running ? "" : "not "); + DPRINTK("pm: %08x: %ld -> %d, %srunning\n", + port->iobase, state->pm->state, pm_state, running ? "" : "not "); if (pm_state == 0) { if (ops->pm) @@ -2051,42 +2056,33 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); -#ifdef CONFIG_PM +#if 0 //def CONFIG_PM /* * Power down all ports by default, except the * console if we have one. We need to drop the * port semaphore here. */ if (state->pm && (!drv->cons || port->line != drv->cons->index)) { - up(&port_sem); pm_send(state->pm, PM_SUSPEND, (void *)3); - down(&port_sem); } #endif } } /* - * Hangup the port. This must be done outside the port_sem - * since uart_hangup() grabs this same semaphore. Grr. + * This reverses the affects of __uart_register_port, hanging up the + * port before removal. */ static void -__uart_hangup_port(struct uart_driver *drv, struct uart_state *state) +__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) { + struct uart_port *port = state->port; struct uart_info *info = state->info; if (info && info->tty) tty_vhangup(info->tty); -} -/* - * This reverses the affects of __uart_register_port. - */ -static void -__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; + down(&state->sem); state->info = NULL; @@ -2113,6 +2109,8 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state) tasklet_kill(&info->tlet); kfree(info); } + + up(&state->sem); } /** @@ -2214,6 +2212,9 @@ int uart_register_driver(struct uart_driver *drv) state->close_delay = 5 * HZ / 10; state->closing_wait = 30 * HZ; + + init_MUTEX(&state->sem); + #ifdef CONFIG_PM state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); if (state->pm) @@ -2305,8 +2306,6 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) printk(KERN_ALERT "Removing wrong port: %p != %p\n", state->port, port); - __uart_hangup_port(drv, state); - down(&port_sem); __uart_unregister_port(drv, state); state->port = NULL; @@ -2408,8 +2407,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) * alter it underneath itself - the port may be open and * trying to do useful work. */ - if (state->count != 0 || - (state->info && state->info->blocked_open != 0)) { + if (uart_users(state) != 0) { ret = -EBUSY; goto out; } @@ -2461,8 +2459,6 @@ void uart_unregister_port(struct uart_driver *drv, int line) state = drv->state + line; - __uart_hangup_port(drv, state); - down(&port_sem); __uart_unregister_port(drv, state); up(&port_sem); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 0ea3262a03b6..859550b04eaa 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -211,6 +211,8 @@ struct uart_state { struct uart_info *info; struct uart_port *port; + struct semaphore sem; + #ifdef CONFIG_PM struct pm_dev *pm; #endif @@ -235,7 +237,6 @@ struct uart_info { */ #define UIF_CHECK_CD (1 << 25) #define UIF_CTS_FLOW (1 << 26) -#define UIF_CLOSING (1 << 27) #define UIF_NORMAL_ACTIVE (1 << 29) #define UIF_INITIALIZED (1 << 31) -- cgit v1.2.3 From b831d0fc5f0bd77041cb2a0daf9d7afbaf3873a3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Mar 2003 15:06:19 +0000 Subject: [SERIAL] Remove remaining notifier-based PM support. --- drivers/serial/core.c | 144 -------------------------------------------- include/linux/serial_core.h | 4 -- 2 files changed, 148 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 54854cb06321..74e91fa51f04 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include /* for serial_state and serial_icounter_struct */ @@ -43,11 +42,6 @@ #define DPRINTK(x...) do { } while (0) #endif -#ifndef CONFIG_PM -#define pm_access(pm) do { } while (0) -#define pm_unregister(pm) do { } while (0) -#endif - /* * This is used to lock changes in serial line configuration. */ @@ -97,8 +91,6 @@ static void uart_start(struct tty_struct *tty) struct uart_port *port = state->port; unsigned long flags; - pm_access(state->pm); - spin_lock_irqsave(&port->lock, flags); __uart_start(tty); spin_unlock_irqrestore(&port->lock, flags); @@ -1878,116 +1870,6 @@ uart_set_options(struct uart_port *port, struct console *co, } #endif /* CONFIG_SERIAL_CORE_CONSOLE */ -#ifdef CONFIG_PM -/* - * Serial port power management. - * - * This is pretty coarse at the moment - either all on or all off. We - * should probably some day do finer power management here some day. - * - * We don't actually save any state; the serial driver already has the - * state held internally to re-setup the port when we come out of D3. - */ -static int uart_pm_set_state(struct uart_state *state, int pm_state, int oldstate) -{ - struct uart_port *port; - struct uart_ops *ops; - int running = state->info && - state->info->flags & UIF_INITIALIZED; - - down(&port_sem); - - if (!state->port || state->port->type == PORT_UNKNOWN) { - up(&port_sem); - return 0; - } - - port = state->port; - ops = port->ops; - - DPRINTK("pm: %08x: %ld -> %d, %srunning\n", - port->iobase, state->pm->state, pm_state, running ? "" : "not "); - - if (pm_state == 0) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - if (running) { - /* - * The port lock isn't taken here - - * the port isn't initialised. - */ - ops->set_mctrl(port, 0); - ops->startup(port); - uart_change_speed(state, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port, 0); - spin_unlock_irq(&port->lock); - } - - /* - * Re-enable the console device after suspending. - */ - if (port->cons && port->cons->index == port->line) - port->cons->flags |= CON_ENABLED; - } else if (pm_state == 1) { - if (ops->pm) - ops->pm(port, pm_state, oldstate); - } else { - /* - * Disable the console device before suspending. - */ - if (port->cons && port->cons->index == port->line) - port->cons->flags &= ~CON_ENABLED; - - if (running) { - spin_lock_irq(&port->lock); - ops->stop_tx(port, 0); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); - ops->shutdown(port); - } - if (ops->pm) - ops->pm(port, pm_state, oldstate); - } - up(&port_sem); - - return 0; -} - -/* - * Wakeup support. - */ -static int uart_pm_set_wakeup(struct uart_state *state, int data) -{ - int err = 0; - - if (state->port->ops->set_wake) - err = state->port->ops->set_wake(state->port, data); - - return err; -} - -static int uart_pm(struct pm_dev *dev, pm_request_t rqst, void *data) -{ - struct uart_state *state = dev->data; - int err = 0; - - switch (rqst) { - case PM_SUSPEND: - case PM_RESUME: - err = uart_pm_set_state(state, (int)(long)data, dev->state); - break; - - case PM_SET_WAKEUP: - err = uart_pm_set_wakeup(state, (int)(long)data); - break; - } - return err; -} -#endif - static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) { @@ -2055,17 +1937,6 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, spin_lock_irqsave(&port->lock, flags); port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); - -#if 0 //def CONFIG_PM - /* - * Power down all ports by default, except the - * console if we have one. We need to drop the - * port semaphore here. - */ - if (state->pm && (!drv->cons || port->line != drv->cons->index)) { - pm_send(state->pm, PM_SUSPEND, (void *)3); - } -#endif } } @@ -2214,21 +2085,11 @@ int uart_register_driver(struct uart_driver *drv) state->closing_wait = 30 * HZ; init_MUTEX(&state->sem); - -#ifdef CONFIG_PM - state->pm = pm_register(PM_SYS_DEV, PM_SYS_COM, uart_pm); - if (state->pm) - state->pm->data = state; -#endif } retval = tty_register_driver(normal); out: if (retval < 0) { -#ifdef CONFIG_PM - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); -#endif kfree(normal); kfree(drv->state); kfree(termios); @@ -2247,11 +2108,6 @@ int uart_register_driver(struct uart_driver *drv) */ void uart_unregister_driver(struct uart_driver *drv) { - int i; - - for (i = 0; i < drv->nr; i++) - pm_unregister(drv->state[i].pm); - tty_unregister_driver(drv->tty_driver); kfree(drv->state); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 859550b04eaa..461c871ea991 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -212,10 +212,6 @@ struct uart_state { struct uart_port *port; struct semaphore sem; - -#ifdef CONFIG_PM - struct pm_dev *pm; -#endif }; #define UART_XMIT_SIZE 1024 -- cgit v1.2.3 From 5a0455c49c42337a550c78589d9f4c89b532ce9c Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Mar 2003 15:23:24 +0000 Subject: [SERIAL] Four bug fixes - Preserve per-port flags which are not modifyable from user space. - Only allow DTR, RTS, OUT1 and OUT2 to be controlled from user space. - Don't put the console port into sleep mode when closing a port. - Ensure that we wake up people waiting on modem status changes when we receive a hangup. --- drivers/serial/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 74e91fa51f04..8dcfa65a0312 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -805,7 +805,8 @@ uart_set_info(struct uart_state *state, struct serial_struct *newinfo) port->irq = new_serial.irq; port->uartclk = new_serial.baud_base * 16; - port->flags = new_serial.flags & UPF_CHANGE_MASK; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_serial.flags & UPF_CHANGE_MASK); port->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; @@ -883,6 +884,8 @@ uart_set_modem_info(struct uart_port *port, unsigned int cmd, if (get_user(arg, value)) return -EFAULT; + arg &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2; + set = clear = 0; switch (cmd) { case TIOCMBIS: @@ -1279,7 +1282,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) schedule_timeout(state->close_delay); set_current_state(TASK_RUNNING); } - } else { + } else if (!port->cons || port->cons->index != port->line) { if (port->ops->pm) port->ops->pm(port, 3, 0); } @@ -1374,6 +1377,7 @@ static void uart_hangup(struct tty_struct *tty) state->info->flags &= ~UIF_NORMAL_ACTIVE; state->info->tty = NULL; wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); } up(&state->sem); } -- cgit v1.2.3 From 567135651a1e8acbaebdd5e01139a718e6ae6a11 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 9 Mar 2003 15:36:51 +0000 Subject: [SERIAL] Prevent multiple calls to tty_{un,}register_device() There were a couple of cases where we call tty_register_device() multiple times for the same port, notibly when ports are discovered via 8250_pci, 8250_pnp or 8250_cs modules. With devfs configured, this causes a warning. Don't call tty_register_device() multiple times. --- drivers/serial/core.c | 61 +++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 8dcfa65a0312..a7008e0d3405 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -1894,17 +1894,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) } static void -__uart_register_port(struct uart_driver *drv, struct uart_state *state, - struct uart_port *port) +uart_configure_port(struct uart_driver *drv, struct uart_state *state, + struct uart_port *port) { unsigned int flags; - state->port = port; - - spin_lock_init(&port->lock); - port->cons = drv->cons; - port->info = state->info; - /* * If there isn't a port here, don't do anything further. */ @@ -1923,12 +1917,6 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, port->ops->config_port(port, flags); } - /* - * Register the port whether it's detected or not. This allows - * setserial to be used to alter this ports parameters. - */ - tty_register_device(drv->tty_driver, drv->minor + port->line); - if (port->type != PORT_UNKNOWN) { unsigned long flags; @@ -1945,11 +1933,11 @@ __uart_register_port(struct uart_driver *drv, struct uart_state *state, } /* - * This reverses the affects of __uart_register_port, hanging up the + * This reverses the affects of uart_configure_port, hanging up the * port before removal. */ static void -__uart_unregister_port(struct uart_driver *drv, struct uart_state *state) +uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) { struct uart_port *port = state->port; struct uart_info *info = state->info; @@ -1961,11 +1949,6 @@ __uart_unregister_port(struct uart_driver *drv, struct uart_state *state) state->info = NULL; - /* - * Remove the devices from devfs - */ - tty_unregister_device(drv->tty_driver, drv->minor + port->line); - /* * Free the port IO and memory resources, if any. */ @@ -2132,6 +2115,7 @@ void uart_unregister_driver(struct uart_driver *drv) int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state; + int ret = 0; BUG_ON(in_interrupt()); @@ -2141,10 +2125,29 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; down(&port_sem); - __uart_register_port(drv, state, port); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + uart_configure_port(drv, state, port); + + /* + * Register the port whether it's detected or not. This allows + * setserial to be used to alter this ports parameters. + */ + tty_register_device(drv->tty_driver, drv->minor + port->line); + + out: up(&port_sem); - return 0; + return ret; } /** @@ -2167,7 +2170,13 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) state->port, port); down(&port_sem); - __uart_unregister_port(drv, state); + + /* + * Remove the devices from devfs + */ + tty_unregister_device(drv->tty_driver, drv->minor + port->line); + + uart_unconfigure_port(drv, state); state->port = NULL; up(&port_sem); @@ -2287,7 +2296,7 @@ int uart_register_port(struct uart_driver *drv, struct uart_port *port) state->port->line = state - drv->state; state->port->mapbase = port->mapbase; - __uart_register_port(drv, state, state->port); + uart_configure_port(drv, state, state->port); } ret = state->port->line; @@ -2320,7 +2329,7 @@ void uart_unregister_port(struct uart_driver *drv, int line) state = drv->state + line; down(&port_sem); - __uart_unregister_port(drv, state); + uart_unconfigure_port(drv, state); up(&port_sem); } -- cgit v1.2.3 From 600d3caf1f7ab7ba7823eeb938b106278d4e10c7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Mar 2003 01:02:44 -0800 Subject: [PATCH] USB: added support for the palm M100 Thanks to C Falconer for the information. --- drivers/usb/serial/visor.c | 3 +++ drivers/usb/serial/visor.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index ee5917af0daa..a0175209ea5b 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -198,6 +198,8 @@ static struct usb_device_id id_table [] = { .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID), @@ -230,6 +232,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PALM_VENDOR_ID, PALM_M505_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M515_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_I705_ID) }, + { USB_DEVICE(PALM_VENDOR_ID, PALM_M100_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M125_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_M130_ID) }, { USB_DEVICE(PALM_VENDOR_ID, PALM_TUNGSTEN_T_ID) }, diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 86f8f3fb642a..6d282c590d6e 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -31,6 +31,7 @@ #define PALM_TUNGSTEN_T_ID 0x0060 #define PALM_TUNGSTEN_Z_ID 0x0031 #define PALM_ZIRE_ID 0x0070 +#define PALM_M100_ID 0x0080 #define SONY_VENDOR_ID 0x054C #define SONY_CLIE_3_5_ID 0x0038 -- cgit v1.2.3 From 2c1490f1e6aa4ecbf54971ed770d0228f3c74fbe Mon Sep 17 00:00:00 2001 From: Duncan Sands Date: Tue, 11 Mar 2003 01:13:26 -0800 Subject: [PATCH] USB speedtouch: send path optimization Write multiple cells in one function call, rather than one cell per function call. Under maximum send load, this reduces cell writing CPU usage from 0.0095% to 0.0085% on my machine. A 10% improvement! :) --- drivers/usb/misc/speedtouch.c | 68 ++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c index 0c4ce9d19612..0a6a227084f1 100644 --- a/drivers/usb/misc/speedtouch.c +++ b/drivers/usb/misc/speedtouch.c @@ -273,39 +273,60 @@ static void udsl_groom_skb (struct atm_vcc *vcc, struct sk_buff *skb) { ctrl->aal5_trailer [7] = crc; } -static char *udsl_write_cell (struct sk_buff *skb, char *target) { +unsigned int udsl_write_cells (unsigned int howmany, struct sk_buff *skb, unsigned char **target_p) { struct udsl_control *ctrl = UDSL_SKB (skb); + unsigned char *target = *target_p; + unsigned int nc, ne, i; - ctrl->num_cells--; + dbg ("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding); - memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); - target += ATM_CELL_HEADER; + nc = ctrl->num_cells; + ne = min (howmany, ctrl->num_entire); - if (ctrl->num_entire) { - ctrl->num_entire--; + for (i = 0; i < ne; i++) { + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; memcpy (target, skb->data, ATM_CELL_PAYLOAD); target += ATM_CELL_PAYLOAD; __skb_pull (skb, ATM_CELL_PAYLOAD); - return target; } + ctrl->num_entire -= ne; + + if (!(ctrl->num_cells -= ne) || !(howmany -= ne)) + goto out; + + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; memcpy (target, skb->data, skb->len); target += skb->len; __skb_pull (skb, skb->len); - memset (target, 0, ctrl->pdu_padding); target += ctrl->pdu_padding; - if (ctrl->num_cells) { - ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; - } else { - memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); - target += ATM_AAL5_TRAILER; - /* set pti bit in last cell */ - *(target + 3 - ATM_CELL_SIZE) |= 0x2; + if (--ctrl->num_cells) { + if (!--howmany) { + ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + goto out; + } + + memcpy (target, ctrl->cell_header, ATM_CELL_HEADER); + target += ATM_CELL_HEADER; + memset (target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER); + target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER; + + if (--ctrl->num_cells) + BUG(); } - return target; + memcpy (target, ctrl->aal5_trailer, ATM_AAL5_TRAILER); + target += ATM_AAL5_TRAILER; + /* set pti bit in last cell */ + *(target + 3 - ATM_CELL_SIZE) |= 0x2; + +out: + *target_p = target; + return nc - ctrl->num_cells; } @@ -500,14 +521,12 @@ static void udsl_complete_send (struct urb *urb, struct pt_regs *regs) static void udsl_process_send (unsigned long data) { struct udsl_send_buffer *buf; - unsigned int cells_to_write; int err; unsigned long flags; - unsigned int i; struct udsl_instance_data *instance = (struct udsl_instance_data *) data; + unsigned int num_written; struct sk_buff *skb; struct udsl_sender *snd; - unsigned char *target; dbg ("udsl_process_send entered"); @@ -577,16 +596,11 @@ made_progress: instance->current_buffer = buf; } - cells_to_write = min (buf->free_cells, UDSL_SKB (skb)->num_cells); - target = buf->free_start; - - dbg ("writing %u cells from skb 0x%p to buffer 0x%p", cells_to_write, skb, buf); + num_written = udsl_write_cells (buf->free_cells, skb, &buf->free_start); - for (i = 0; i < cells_to_write; i++) - target = udsl_write_cell (skb, target); + dbg ("wrote %u cells from skb 0x%p to buffer 0x%p", num_written, skb, buf); - buf->free_start = target; - if (!(buf->free_cells -= cells_to_write)) { + if (!(buf->free_cells -= num_written)) { list_add_tail (&buf->list, &instance->filled_buffers); instance->current_buffer = NULL; dbg ("queued filled buffer"); -- cgit v1.2.3 From a6fac0ad08608fd79ad2271a0ab18e7e3a50947a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Mar 2003 01:16:12 -0800 Subject: [PATCH] USB: fix up a comment in usb_unlink() Thanks to David for pointing this out. --- drivers/usb/core/urb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index a33c33b08082..18b44d3cd9a7 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -383,7 +383,7 @@ int usb_unlink_urb(struct urb *urb) { /* FIXME * We should not care about the state here, but the host controllers - * die a horrible death if we submit a urb for a device that has been + * die a horrible death if we unlink a urb for a device that has been * physically removed. */ if (urb && -- cgit v1.2.3 From 5ffbfe1e2d6016e3ed6485663ca745edc81d59a9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 11 Mar 2003 01:35:29 -0800 Subject: [PATCH] USB: Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl for the information. --- drivers/usb/serial/visor.c | 7 +++++++ drivers/usb/serial/visor.h | 1 + 2 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index a0175209ea5b..22b4ef591a81 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -12,6 +12,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (03/09/2003) gkh + * Added support for the Sony Clie NZ90V device. Thanks to Martin Brachtl + * for the information. + * * (03/05/2003) gkh * Think Treo support is now working. * @@ -217,6 +221,8 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID), .driver_info = (kernel_ulong_t)&palm_os_4_probe }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID), + .driver_info = (kernel_ulong_t)&palm_os_4_probe }, { } /* Terminating entry */ }; @@ -243,6 +249,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_S360_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 6d282c590d6e..089bcf98da86 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -39,6 +39,7 @@ #define SONY_CLIE_S360_ID 0x0095 #define SONY_CLIE_4_1_ID 0x009A #define SONY_CLIE_NX60_ID 0x00DA +#define SONY_CLIE_NZ90V_ID 0x00E9 /**************************************************************************** * Handspring Visor Vendor specific request codes (bRequest values) -- cgit v1.2.3 From cd9a1f5f2aeabee7645fa56a2ecca5bd0465ddc9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 11 Mar 2003 03:13:16 -0800 Subject: [PATCH] unplugging fix Patch from Neil Brown Allow auto-unplugging to work for devices that do it themselves. Auto-unplugging - and blk_unplug_work in particular - assumes that the device uses "generic_unplug_device" for unplugging, but some devices don't. md crashes. So blk_unplug_work should use ->unplug_fn --- drivers/block/ll_rw_blk.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 691ccc1f0d60..9d64f8cc690d 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1040,7 +1040,8 @@ void generic_unplug_device(void *data) static void blk_unplug_work(void *data) { - generic_unplug_device(data); + request_queue_t *q = data; + q->unplug_fn(q); } static void blk_unplug_timeout(unsigned long data) -- cgit v1.2.3 From fc93ade6d7cfaa46422d414d55320c696a482d19 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Mar 2003 14:18:36 +0000 Subject: [SERIAL] Add new device model based power management infrastructure. Add uart_suspend_port(), uart_resume_port() which are responsible for suspending/resuming one UART port. Low level drivers are expected to call this from their device model suspend and resume methods. --- drivers/serial/core.c | 114 +++++++++++++++++++++++++++++++++++++++++--- include/linux/serial_core.h | 7 +++ 2 files changed, 114 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index a7008e0d3405..d6a249a32d71 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include /* for serial_state and serial_icounter_struct */ #include @@ -53,6 +54,7 @@ static DECLARE_MUTEX(port_sem); static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void uart_change_pm(struct uart_state *state, int pm_state); /* * This routine is used by the interrupt handler to schedule processing in @@ -1283,8 +1285,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) set_current_state(TASK_RUNNING); } } else if (!port->cons || port->cons->index != port->line) { - if (port->ops->pm) - port->ops->pm(port, 3, 0); + uart_change_pm(state, 3); } /* @@ -1613,11 +1614,8 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * Make sure the device is in D0 state. */ - if (state->count == 1) { - struct uart_port *port = state->port; - if (port->ops->pm) - port->ops->pm(port, 0, 3); - } + if (state->count == 1) + uart_change_pm(state, 0); /* * Start up the serial port. @@ -1874,6 +1872,99 @@ uart_set_options(struct uart_port *port, struct console *co, } #endif /* CONFIG_SERIAL_CORE_CONSOLE */ +static void uart_change_pm(struct uart_state *state, int pm_state) +{ + struct uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} + +int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; + + down(&state->sem); + + switch (level) { + case SUSPEND_SAVE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + spin_lock_irq(&port->lock); + ops->stop_tx(port, 0); + ops->set_mctrl(port, 0); + ops->stop_rx(port); + spin_unlock_irq(&port->lock); + + /* + * Wait for the transmitter to empty. + */ + while (!ops->tx_empty(port)) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10*HZ/1000); + } + set_current_state(TASK_RUNNING); + + ops->shutdown(port); + } + break; + + case SUSPEND_POWER_DOWN: + /* + * Disable the console device before suspending. + */ + if (port->cons && port->cons->index == port->line) + port->cons->flags &= ~CON_ENABLED; + + uart_change_pm(state, 3); + break; + } + + up(&state->sem); + + return 0; +} + +int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) +{ + struct uart_state *state = drv->state + port->line; + + down(&state->sem); + + switch (level) { + case RESUME_POWER_ON: + uart_change_pm(state, 0); + + /* + * Re-enable the console device after suspending. + */ + if (port->cons && port->cons->index == port->line) { + uart_change_speed(state, NULL); + port->cons->flags |= CON_ENABLED; + } + break; + + case RESUME_RESTORE_STATE: + if (state->info && state->info->flags & UIF_INITIALIZED) { + struct uart_ops *ops = port->ops; + + ops->set_mctrl(port, 0); + ops->startup(port); + uart_change_speed(state, NULL); + spin_lock_irq(&port->lock); + ops->set_mctrl(port, port->mctrl); + ops->start_tx(port, 0); + spin_unlock_irq(&port->lock); + } + break; + } + + up(&state->sem); + + return 0; +} + static inline void uart_report_port(struct uart_driver *drv, struct uart_port *port) { @@ -1929,6 +2020,13 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, spin_lock_irqsave(&port->lock, flags); port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); + + /* + * Power down all ports by default, except the + * console if we have one. + */ + if (!port->cons || port->cons->index != port->line) + uart_change_pm(state, 3); } } @@ -2336,6 +2434,8 @@ void uart_unregister_port(struct uart_driver *drv, int line) EXPORT_SYMBOL(uart_write_wakeup); EXPORT_SYMBOL(uart_register_driver); EXPORT_SYMBOL(uart_unregister_driver); +EXPORT_SYMBOL(uart_suspend_port); +EXPORT_SYMBOL(uart_resume_port); EXPORT_SYMBOL(uart_register_port); EXPORT_SYMBOL(uart_unregister_port); EXPORT_SYMBOL(uart_add_one_port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 461c871ea991..80ba519dda77 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -208,6 +208,7 @@ struct uart_state { #define USF_CLOSING_WAIT_NONE (65535) int count; + int pm_state; struct uart_info *info; struct uart_port *port; @@ -302,6 +303,12 @@ int uart_register_port(struct uart_driver *reg, struct uart_port *port); int uart_add_one_port(struct uart_driver *reg, struct uart_port *port); int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port); +/* + * Power Management + */ +int uart_suspend_port(struct uart_driver *reg, struct uart_port *port, u32 level); +int uart_resume_port(struct uart_driver *reg, struct uart_port *port, u32 level); + #define uart_circ_empty(circ) ((circ)->head == (circ)->tail) #define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) -- cgit v1.2.3 From dcb96e76bcc6c727fe45e722d2709aaca009774f Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Mar 2003 21:09:23 +0000 Subject: [SERIAL] Add sa1100 serial PM support using device model. --- drivers/serial/sa1100.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index cf3160b26f57..b32608e75444 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -857,12 +858,54 @@ static struct uart_driver sa1100_reg = { .cons = SA1100_CONSOLE, }; +static int sa1100_serial_suspend(struct device *dev, u32 state, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_suspend_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static int sa1100_serial_resume(struct device *dev, u32 level) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + uart_resume_port(&sa1100_reg, &sa1100_ports[i].port, level); + + return 0; +} + +static struct device_driver sa11x0_serial_driver = { + .name = "sa11x0_serial", + .bus = &system_bus_type, + .devclass = &tty_devclass, + .suspend = sa1100_serial_suspend, + .resume = sa1100_serial_resume, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices. + */ +static struct sys_device sa11x0_serial_devs = { + .name = "sa11x0_serial", + .id = 0, + .dev = { + .driver = &sa11x0_serial_driver, + }, +}; + static int __init sa1100_serial_init(void) { int ret; printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n"); + driver_register(&sa11x0_serial_driver); + sys_device_register(&sa11x0_serial_devs); + sa1100_init_ports(); ret = uart_register_driver(&sa1100_reg); if (ret == 0) { -- cgit v1.2.3 From 1ae23b0daffc3b0f6c2d6fadb6291638c3d0760d Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Mar 2003 21:17:15 +0000 Subject: [SERIAL] Add uart_console(port) macro. --- drivers/serial/core.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index d6a249a32d71..18d74e98db4b 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -52,6 +52,12 @@ static DECLARE_MUTEX(port_sem); #define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) +#ifdef CONFIG_SERIAL_CORE_CONSOLE +#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) +#else +#define uart_console(port) (0) +#endif + static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); @@ -1284,7 +1290,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp) schedule_timeout(state->close_delay); set_current_state(TASK_RUNNING); } - } else if (!port->cons || port->cons->index != port->line) { + } else if (!uart_console(port)) { uart_change_pm(state, 3); } @@ -1392,15 +1398,12 @@ static void uart_hangup(struct tty_struct *tty) static void uart_update_termios(struct uart_state *state) { struct tty_struct *tty = state->info->tty; + struct uart_port *port = state->port; -#ifdef CONFIG_SERIAL_CORE_CONSOLE - struct console *c = state->port->cons; - - if (c && c->cflag && c->index == state->port->line) { - tty->termios->c_cflag = c->cflag; - c->cflag = 0; + if (uart_console(port)) { + tty->termios->c_cflag = port->cons->cflag; + port->cons->cflag = 0; } -#endif /* * If the device failed to grab its irq resources, @@ -1417,7 +1420,7 @@ static void uart_update_termios(struct uart_state *state) * And finally enable the RTS and DTR signals. */ if (tty->termios->c_cflag & CBAUD) - uart_set_mctrl(state->port, TIOCM_DTR | TIOCM_RTS); + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); } } @@ -1914,7 +1917,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port, u32 level /* * Disable the console device before suspending. */ - if (port->cons && port->cons->index == port->line) + if (uart_console(port)) port->cons->flags &= ~CON_ENABLED; uart_change_pm(state, 3); @@ -1939,7 +1942,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port, u32 level) /* * Re-enable the console device after suspending. */ - if (port->cons && port->cons->index == port->line) { + if (uart_console(port)) { uart_change_speed(state, NULL); port->cons->flags |= CON_ENABLED; } @@ -2025,7 +2028,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, * Power down all ports by default, except the * console if we have one. */ - if (!port->cons || port->cons->index != port->line) + if (!uart_console(port)) uart_change_pm(state, 3); } } -- cgit v1.2.3 From 90a2f98b16a1889f6eb21540aba1244f9cf4f4ed Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 11 Mar 2003 22:24:53 +0000 Subject: [SERIAL] Add PCI serial power management support. --- drivers/serial/8250.c | 28 +++++++++++++++++++++++++- drivers/serial/8250.h | 2 ++ drivers/serial/8250_pci.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index be094804cf79..3b719f893e55 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -93,13 +93,15 @@ unsigned int share_irqs = SERIAL8250_SHARE_IRQS; #ifdef CONFIG_SERIAL_8250_MULTIPORT #define CONFIG_SERIAL_MULTIPORT 1 #endif +#ifdef CONFIG_SERIAL_8250_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS 1 +#endif /* * HUB6 is always on. This will be removed once the header * files have been cleaned. */ #define CONFIG_HUB6 1 -#define CONFIG_SERIAL_MANY_PORTS 1 #include @@ -2094,6 +2096,28 @@ void serial8250_get_irq_map(unsigned int *map) } } +/** + * serial8250_suspend_port - suspend one serial port + * @line: serial line number + * + * Suspend one serial port. + */ +void serial8250_suspend_port(int line, u32 level) +{ + uart_suspend_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + +/** + * serial8250_resume_port - resume one serial port + * @line: serial line number + * + * Resume one serial port. + */ +void serial8250_resume_port(int line, u32 level) +{ + uart_resume_port(&serial8250_reg, &serial8250_ports[line].port, level); +} + static int __init serial8250_init(void) { int ret, i; @@ -2127,6 +2151,8 @@ module_exit(serial8250_exit); EXPORT_SYMBOL(register_serial); EXPORT_SYMBOL(unregister_serial); EXPORT_SYMBOL(serial8250_get_irq_map); +EXPORT_SYMBOL(serial8250_suspend_port); +EXPORT_SYMBOL(serial8250_resume_port); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic 8250/16x50 serial driver $Revision: 1.90 $"); diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 030116e6ebf6..cc206f9476ac 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -27,6 +27,8 @@ struct serial8250_probe { int serial8250_register_probe(struct serial8250_probe *probe); void serial8250_unregister_probe(struct serial8250_probe *probe); void serial8250_get_irq_map(unsigned int *map); +void serial8250_suspend_port(int line, u32 level); +void serial8250_resume_port(int line, u32 level); struct old_serial_port { unsigned int uart; diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index b0711e53f471..1123ebd9fe30 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -1592,6 +1592,53 @@ static void __devexit pciserial_remove_one(struct pci_dev *dev) } } +static int pciserial_save_state_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_SAVE_STATE); + } + return 0; +} + +static int pciserial_suspend_one(struct pci_dev *dev, u32 state) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + for (i = 0; i < priv->nr; i++) + serial8250_suspend_port(priv->line[i], SUSPEND_POWER_DOWN); + } + return 0; +} + +static int pciserial_resume_one(struct pci_dev *dev) +{ + struct serial_private *priv = pci_get_drvdata(dev); + + if (priv) { + int i; + + /* + * Ensure that the board is correctly configured. + */ + if (priv->quirk->init) + priv->quirk->init(dev); + + for (i = 0; i < priv->nr; i++) { + serial8250_resume_port(priv->line[i], RESUME_POWER_ON); + serial8250_resume_port(priv->line[i], RESUME_RESTORE_STATE); + } + } + return 0; +} + static struct pci_device_id serial_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, @@ -1986,6 +2033,9 @@ static struct pci_driver serial_pci_driver = { .name = "serial", .probe = pciserial_init_one, .remove = __devexit_p(pciserial_remove_one), + .save_state = pciserial_save_state_one, + .suspend = pciserial_suspend_one, + .resume = pciserial_resume_one, .id_table = serial_pci_tbl, .driver = { .devclass = &tty_devclass, -- cgit v1.2.3 From a800d7ad1c0fd9fe45450145955ab1f98c3d5d69 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Tue, 11 Mar 2003 15:23:15 -0800 Subject: [PATCH] v4l: crunch MIN/MAX macros. This patch deletes the MIN/MAX macros from audiochip.h and fixes all users of these macros to use the kernels min/max macros instead. --- drivers/media/video/audiochip.h | 12 ------------ drivers/media/video/msp3400.c | 8 ++++---- drivers/media/video/tda9875.c | 10 ++++------ drivers/media/video/tvaudio.c | 8 ++++---- drivers/media/video/tvmixer.c | 10 ++++------ 5 files changed, 16 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/audiochip.h b/drivers/media/video/audiochip.h index b161f921d303..e5a5cd2bb172 100644 --- a/drivers/media/video/audiochip.h +++ b/drivers/media/video/audiochip.h @@ -3,9 +3,6 @@ /* ---------------------------------------------------------------------- */ -#define MIN(a,b) (((a)>(b))?(b):(a)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - /* v4l device was opened in Radio mode */ #define AUDC_SET_RADIO _IO('m',2) /* select from TV,radio,extern,MUTE */ @@ -21,15 +18,6 @@ #define AUDIO_MUTE 0x80 #define AUDIO_UNMUTE 0x81 -/* all the stuff below is obsolete and just here for reference. I'll - * remove it once the driver is tested and works fine. - * - * Instead creating alot of tiny API's for all kinds of different - * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not - * yet...). It is a bit less flexible, but most/all used i2c chips - * make sense in v4l context only. So I think that's acceptable... - */ - /* misc stuff to pass around config info to i2c chips */ #define AUDC_CONFIG_PINNACLE _IOW('m',32,int) diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index d91d04f97f4f..7defb547b12c 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -1495,8 +1495,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) VIDEO_AUDIO_MUTABLE; if (msp->muted) va->flags |= VIDEO_AUDIO_MUTE; - va->volume=MAX(msp->left,msp->right); - va->balance=(32768*MIN(msp->left,msp->right))/ + va->volume=max(msp->left,msp->right); + va->balance=(32768*min(msp->left,msp->right))/ (va->volume ? va->volume : 1); va->balance=(msp->leftright)? (65535-va->balance) : va->balance; @@ -1517,9 +1517,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); msp->muted = (va->flags & VIDEO_AUDIO_MUTE); - msp->left = (MIN(65536 - va->balance,32768) * + msp->left = (min(65536 - va->balance,32768) * va->volume) / 32768; - msp->right = (MIN(va->balance,32768) * + msp->right = (min(va->balance,(__u16)32768) * va->volume) / 32768; msp->bass = va->bass; msp->treble = va->treble; diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index d2d0917ececf..51b41a008484 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -316,17 +316,15 @@ static int tda9875_command(struct i2c_client *client, /* min is -84 max is 24 */ left = (t->lvol+84)*606; right = (t->rvol+84)*606; - va->volume=MAX(left,right); - va->balance=(32768*MIN(left,right))/ + va->volume=max(left,right); + va->balance=(32768*min(left,right))/ (va->volume ? va->volume : 1); va->balance=(leftbalance) : va->balance; va->bass = (t->bass+12)*2427; /* min -12 max +15 */ va->treble = (t->treble+12)*2730;/* min -12 max +12 */ - va->mode |= VIDEO_SOUND_MONO; - break; /* VIDIOCGAUDIO case */ } @@ -336,9 +334,9 @@ static int tda9875_command(struct i2c_client *client, int left,right; dprintk("VIDEOCSAUDIO...\n"); - left = (MIN(65536 - va->balance,32768) * + left = (min(65536 - va->balance,32768) * va->volume) / 32768; - right = (MIN(va->balance,32768) * + right = (min(va->balance,(__u16)32768) * va->volume) / 32768; t->lvol = ((left/606)-84) & 0xff; if (t->lvol > 24) diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index b077e42ef792..9bd14e322539 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1477,8 +1477,8 @@ static int chip_command(struct i2c_client *client, if (desc->flags & CHIP_HAS_VOLUME) { va->flags |= VIDEO_AUDIO_VOLUME; - va->volume = MAX(chip->left,chip->right); - va->balance = (32768*MIN(chip->left,chip->right))/ + va->volume = max(chip->left,chip->right); + va->balance = (32768*min(chip->left,chip->right))/ (va->volume ? va->volume : 1); } if (desc->flags & CHIP_HAS_BASSTREBLE) { @@ -1500,9 +1500,9 @@ static int chip_command(struct i2c_client *client, struct video_audio *va = arg; if (desc->flags & CHIP_HAS_VOLUME) { - chip->left = (MIN(65536 - va->balance,32768) * + chip->left = (min(65536 - va->balance,32768) * va->volume) / 32768; - chip->right = (MIN(va->balance,32768) * + chip->right = (min(va->balance,(__u16)32768) * va->volume) / 32768; chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index 73aba5038825..ff50ba8bb249 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -16,8 +16,6 @@ #include #include -#include "audiochip.h" -#include "id.h" #define DEV_MAX 4 @@ -136,16 +134,16 @@ static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cm case MIXER_WRITE(SOUND_MIXER_VOLUME): left = mix_to_v4l(val); right = mix_to_v4l(val >> 8); - va.volume = MAX(left,right); - va.balance = (32768*MIN(left,right)) / (va.volume ? va.volume : 1); + va.volume = max(left,right); + va.balance = (32768*min(left,right)) / (va.volume ? va.volume : 1); va.balance = (leftdriver->command(client,VIDIOCSAUDIO,&va); client->driver->command(client,VIDIOCGAUDIO,&va); /* fall throuth */ case MIXER_READ(SOUND_MIXER_VOLUME): - left = (MIN(65536 - va.balance,32768) * + left = (min(65536 - va.balance,32768) * va.volume) / 32768; - right = (MIN(va.balance,32768) * + right = (min(va.balance,32768) * va.volume) / 32768; ret = v4l_to_mix2(left,right); break; -- cgit v1.2.3 From c9d044cb19ed64ccf26a38cc05651ca3441d4438 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Tue, 11 Mar 2003 15:25:48 -0800 Subject: [PATCH] v4l: create include/media This patch creates a new include directory include/media, populates it with a few files header files and fixups the affected drivers to compile with the new directory layout. The directory is intented to be used for (kernel-internal) header files of the media drivers (which are sitting below drivers/media). For now the video-buf.h (mm helper), tuner.h (tv/radio tuner) and audiochip.h (tv sound decoder drivers) header files are moved. Some more header files from the dvb folks will likely follow. --- drivers/media/video/audiochip.h | 24 --- drivers/media/video/bttv-cards.c | 1 - drivers/media/video/bttv-driver.c | 1 - drivers/media/video/bttv-if.c | 1 - drivers/media/video/bttvp.h | 6 +- drivers/media/video/id.h | 35 ----- drivers/media/video/msp3400.c | 2 +- drivers/media/video/saa7134/saa7134-cards.c | 1 - drivers/media/video/saa7134/saa7134-core.c | 1 - drivers/media/video/saa7134/saa7134-i2c.c | 2 - drivers/media/video/saa7134/saa7134-video.c | 2 - drivers/media/video/saa7134/saa7134.h | 6 +- drivers/media/video/tda7432.c | 4 +- drivers/media/video/tda9875.c | 4 +- drivers/media/video/tda9887.c | 4 +- drivers/media/video/tuner-3036.c | 2 +- drivers/media/video/tuner.c | 4 +- drivers/media/video/tuner.h | 94 ----------- drivers/media/video/tvaudio.c | 5 +- drivers/media/video/video-buf.c | 2 +- drivers/media/video/video-buf.h | 234 ---------------------------- include/media/audiochip.h | 24 +++ include/media/id.h | 35 +++++ include/media/tuner.h | 94 +++++++++++ include/media/video-buf.h | 234 ++++++++++++++++++++++++++++ 25 files changed, 410 insertions(+), 412 deletions(-) delete mode 100644 drivers/media/video/audiochip.h delete mode 100644 drivers/media/video/id.h delete mode 100644 drivers/media/video/tuner.h delete mode 100644 drivers/media/video/video-buf.h create mode 100644 include/media/audiochip.h create mode 100644 include/media/id.h create mode 100644 include/media/tuner.h create mode 100644 include/media/video-buf.h (limited to 'drivers') diff --git a/drivers/media/video/audiochip.h b/drivers/media/video/audiochip.h deleted file mode 100644 index e5a5cd2bb172..000000000000 --- a/drivers/media/video/audiochip.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef AUDIOCHIP_H -#define AUDIOCHIP_H - -/* ---------------------------------------------------------------------- */ - -/* v4l device was opened in Radio mode */ -#define AUDC_SET_RADIO _IO('m',2) -/* select from TV,radio,extern,MUTE */ -#define AUDC_SET_INPUT _IOW('m',17,int) - -/* audio inputs */ -#define AUDIO_TUNER 0x00 -#define AUDIO_RADIO 0x01 -#define AUDIO_EXTERN 0x02 -#define AUDIO_INTERN 0x03 -#define AUDIO_OFF 0x04 -#define AUDIO_ON 0x05 -#define AUDIO_MUTE 0x80 -#define AUDIO_UNMUTE 0x81 - -/* misc stuff to pass around config info to i2c chips */ -#define AUDC_CONFIG_PINNACLE _IOW('m',32,int) - -#endif /* AUDIOCHIP_H */ diff --git a/drivers/media/video/bttv-cards.c b/drivers/media/video/bttv-cards.c index 2ed590fc87c1..0f823e82d5e3 100644 --- a/drivers/media/video/bttv-cards.c +++ b/drivers/media/video/bttv-cards.c @@ -36,7 +36,6 @@ #include #include "bttvp.h" -#include "tuner.h" #include "bt832.h" /* fwd decl */ diff --git a/drivers/media/video/bttv-driver.c b/drivers/media/video/bttv-driver.c index b9d3949f9857..ed8d3b0f03f6 100644 --- a/drivers/media/video/bttv-driver.c +++ b/drivers/media/video/bttv-driver.c @@ -37,7 +37,6 @@ #include #include "bttvp.h" -#include "tuner.h" int bttv_num; /* number of Bt848s in use */ struct bttv bttvs[BTTV_MAX]; diff --git a/drivers/media/video/bttv-if.c b/drivers/media/video/bttv-if.c index 01ea37beac5f..1b3c83e16fe0 100644 --- a/drivers/media/video/bttv-if.c +++ b/drivers/media/video/bttv-if.c @@ -34,7 +34,6 @@ #include #include "bttvp.h" -#include "tuner.h" static struct i2c_algo_bit_data bttv_i2c_algo_template; static struct i2c_adapter bttv_i2c_adap_template; diff --git a/drivers/media/video/bttvp.h b/drivers/media/video/bttvp.h index 49882b03769e..65f8d54e1816 100644 --- a/drivers/media/video/bttvp.h +++ b/drivers/media/video/bttvp.h @@ -34,10 +34,12 @@ #include #include +#include +#include +#include + #include "bt848.h" #include "bttv.h" -#include "video-buf.h" -#include "audiochip.h" #ifdef __KERNEL__ diff --git a/drivers/media/video/id.h b/drivers/media/video/id.h deleted file mode 100644 index f9360b0665bb..000000000000 --- a/drivers/media/video/id.h +++ /dev/null @@ -1,35 +0,0 @@ -/* FIXME: this temporarely, until these are included in linux/i2c-id.h */ - -/* drivers */ -#ifndef I2C_DRIVERID_TVMIXER -# define I2C_DRIVERID_TVMIXER I2C_DRIVERID_EXP0 -#endif -#ifndef I2C_DRIVERID_TVAUDIO -# define I2C_DRIVERID_TVAUDIO I2C_DRIVERID_EXP1 -#endif - -/* chips */ -#ifndef I2C_DRIVERID_DPL3518 -# define I2C_DRIVERID_DPL3518 I2C_DRIVERID_EXP2 -#endif -#ifndef I2C_DRIVERID_TDA9873 -# define I2C_DRIVERID_TDA9873 I2C_DRIVERID_EXP3 -#endif -#ifndef I2C_DRIVERID_TDA9875 -# define I2C_DRIVERID_TDA9875 I2C_DRIVERID_EXP0+4 -#endif -#ifndef I2C_DRIVERID_PIC16C54_PV951 -# define I2C_DRIVERID_PIC16C54_PV951 I2C_DRIVERID_EXP0+5 -#endif -#ifndef I2C_DRIVERID_TDA7432 -# define I2C_DRIVERID_TDA7432 I2C_DRIVERID_EXP0+6 -#endif -#ifndef I2C_DRIVERID_TDA9874 -# define I2C_DRIVERID_TDA9874 I2C_DRIVERID_EXP0+7 -#endif - -/* algorithms */ -#ifndef I2C_ALGO_SAA7134 -# define I2C_ALGO_SAA7134 0x090000 -#endif - diff --git a/drivers/media/video/msp3400.c b/drivers/media/video/msp3400.c index 7defb547b12c..9000e6f107c3 100644 --- a/drivers/media/video/msp3400.c +++ b/drivers/media/video/msp3400.c @@ -56,7 +56,7 @@ #define __KERNEL_SYSCALLS__ #include -#include "audiochip.h" +#include #include "msp3400.h" /* Addresses to scan */ diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index bc8b98bfd062..d2d58ec70da1 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -24,7 +24,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" /* commly used strings */ static char name_mute[] = "mute"; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 07b31bc2f848..e88367522cdb 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -30,7 +30,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 438ee56ae0c3..72463f5cb7b3 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -30,8 +30,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" -#include "id.h" /* ----------------------------------------------------------- */ diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 562ebf3332ee..9d22bf8a348b 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -29,8 +29,6 @@ #include "saa7134-reg.h" #include "saa7134.h" -#include "tuner.h" -#include "audiochip.h" /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index aa3ef2596b62..57f1fc42ed0a 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -22,7 +22,11 @@ #include #include #include -#include "video-buf.h" + +#include +#include +#include +#include #define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,2) diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 4b96ec8af1f7..8ba55c0a6644 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -49,8 +49,8 @@ #include #include "bttv.h" -#include "audiochip.h" -#include "id.h" +#include +#include #ifndef VIDEO_AUDIO_BALANCE # define VIDEO_AUDIO_BALANCE 32 diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 51b41a008484..28a9c2f60c7d 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -31,8 +31,8 @@ #include #include "bttv.h" -#include "audiochip.h" -#include "id.h" +#include +#include MODULE_PARM(debug,"i"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index 3d8c6fa0b20f..fb20ba56c24e 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -7,8 +7,8 @@ #include #include -#include "id.h" -#include "audiochip.h" +#include +#include /* Chips: TDA9885 (PAL, NTSC) diff --git a/drivers/media/video/tuner-3036.c b/drivers/media/video/tuner-3036.c index 4319f317cb30..5c4328d6a1ab 100644 --- a/drivers/media/video/tuner-3036.c +++ b/drivers/media/video/tuner-3036.c @@ -27,7 +27,7 @@ #include #include -#include "tuner.h" +#include static int debug; /* insmod parameter */ static int this_adap; diff --git a/drivers/media/video/tuner.c b/drivers/media/video/tuner.c index 0638747257c5..f83309c257d8 100644 --- a/drivers/media/video/tuner.c +++ b/drivers/media/video/tuner.c @@ -12,8 +12,8 @@ #include #include -#include "tuner.h" -#include "audiochip.h" +#include +#include /* Addresses to scan */ static unsigned short normal_i2c[] = {I2C_CLIENT_END}; diff --git a/drivers/media/video/tuner.h b/drivers/media/video/tuner.h deleted file mode 100644 index f716e23dda8e..000000000000 --- a/drivers/media/video/tuner.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - tuner.h - definition for different tuners - - Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de) - minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de) - - 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. -*/ - -#ifndef _TUNER_H -#define _TUNER_H - -#include "id.h" - -#define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ -#define TUNER_PHILIPS_PAL_I 1 -#define TUNER_PHILIPS_NTSC 2 -#define TUNER_PHILIPS_SECAM 3 /* you must actively select B/G, L, L` */ -#define TUNER_ABSENT 4 -#define TUNER_PHILIPS_PAL 5 -#define TUNER_TEMIC_NTSC 6 /* 4032 FY5 (3X 7004, 9498, 9789) */ -#define TUNER_TEMIC_PAL_I 7 /* 4062 FY5 (3X 8501, 9957) */ -#define TUNER_TEMIC_4036FY5_NTSC 8 /* 4036 FY5 (3X 1223, 1981, 7686) */ -#define TUNER_ALPS_TSBH1_NTSC 9 -#define TUNER_ALPS_TSBE1_PAL 10 -#define TUNER_ALPS_TSBB5_PAL_I 11 -#define TUNER_ALPS_TSBE5_PAL 12 -#define TUNER_ALPS_TSBC5_PAL 13 -#define TUNER_TEMIC_4006FH5_PAL 14 /* 4006 FH5 (3X 9500, 9501, 7291) */ -#define TUNER_ALPS_TSHC6_NTSC 15 -#define TUNER_TEMIC_PAL_DK 16 /* 4016 FY5 (3X 1392, 1393) */ -#define TUNER_PHILIPS_NTSC_M 17 -#define TUNER_TEMIC_4066FY5_PAL_I 18 /* 4066 FY5 (3X 7032, 7035) */ -#define TUNER_TEMIC_4006FN5_MULTI_PAL 19 /* B/G, I and D/K autodetected (3X 7595, 7606, 7657)*/ -#define TUNER_TEMIC_4009FR5_PAL 20 /* incl. FM radio (3X 7607, 7488, 7711)*/ -#define TUNER_TEMIC_4039FR5_NTSC 21 /* incl. FM radio (3X 7246, 7578, 7732)*/ -#define TUNER_TEMIC_4046FM5 22 /* you must actively select B/G, D/K, I, L, L` ! (3X 7804, 7806, 8103, 8104)*/ -#define TUNER_PHILIPS_PAL_DK 23 -#define TUNER_PHILIPS_FQ1216ME 24 /* you must actively select B/G/D/K, I, L, L` */ -#define TUNER_LG_PAL_I_FM 25 -#define TUNER_LG_PAL_I 26 -#define TUNER_LG_NTSC_FM 27 -#define TUNER_LG_PAL_FM 28 -#define TUNER_LG_PAL 29 -#define TUNER_TEMIC_4009FN5_MULTI_PAL_FM 30 /* B/G, I and D/K autodetected (3X 8155, 8160, 8163)*/ -#define TUNER_SHARP_2U5JF5540_NTSC 31 -#define TUNER_Samsung_PAL_TCPM9091PD27 32 -#define TUNER_MT2032 33 -#define TUNER_TEMIC_4106FH5 34 /* 4106 FH5 (3X 7808, 7865)*/ -#define TUNER_TEMIC_4012FY5 35 /* 4012 FY5 (3X 0971, 1099)*/ -#define TUNER_TEMIC_4136FY5 36 /* 4136 FY5 (3X 7708, 7746)*/ -#define TUNER_LG_PAL_NEW_TAPC 37 -#define TUNER_PHILIPS_FM1216ME_MK3 38 -#define TUNER_LG_NTSC_NEW_TAPC 39 - - - - -#define NOTUNER 0 -#define PAL 1 /* PAL_BG */ -#define PAL_I 2 -#define NTSC 3 -#define SECAM 4 - -#define NoTuner 0 -#define Philips 1 -#define TEMIC 2 -#define Sony 3 -#define Alps 4 -#define LGINNOTEK 5 -#define SHARP 6 -#define Samsung 7 -#define Microtune 8 - -#define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ -#define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ -#if 0 /* obsolete */ -# define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ -# define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ -#endif - -#endif diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 9bd14e322539..bf68fc0d6874 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -29,9 +29,10 @@ #include #include -#include "audiochip.h" +#include +#include + #include "tvaudio.h" -#include "id.h" /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 20e2c2e6f692..36c155bfd42f 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -31,7 +31,7 @@ # define TryLockPage TestSetPageLocked #endif -#include "video-buf.h" +#include static int debug = 0; diff --git a/drivers/media/video/video-buf.h b/drivers/media/video/video-buf.h deleted file mode 100644 index fceea3e24f75..000000000000 --- a/drivers/media/video/video-buf.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * generic helper functions for video4linux capture buffers, to handle - * memory management and PCI DMA. Right now bttv + saa7134 use it. - * - * The functions expect the hardware being able to scatter gatter - * (i.e. the buffers are not linear in physical memory, but fragmented - * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data (thus it is probably not useful for USB as - * data often must be uncompressed by the drivers). - * - * (c) 2001,02 Gerd Knorr - * - * 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. - */ - -#include - -/* --------------------------------------------------------------------- */ - -/* - * Return a scatterlist for some page-aligned vmalloc()'ed memory - * block (NULL on errors). Memory for the scatterlist is allocated - * using kmalloc. The caller must free the memory. - */ -struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); - -/* - * Return a scatterlist for a an array of userpages (NULL on errors). - * Memory for the scatterlist is allocated using kmalloc. The caller - * must free the memory. - */ -struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, - int offset); -int videobuf_lock(struct page **pages, int nr_pages); -int videobuf_unlock(struct page **pages, int nr_pages); - -/* --------------------------------------------------------------------- */ - -/* - * A small set of helper functions to manage buffers (both userland - * and kernel) for DMA. - * - * videobuf_init_*_dmabuf() - * creates a buffer. The userland version takes a userspace - * pointer + length. The kernel version just wants the size and - * does memory allocation too using vmalloc_32(). - * - * videobuf_pci_*_dmabuf() - * see Documentation/DMA-mapping.txt, these functions to - * basically the same. The map function does also build a - * scatterlist for the buffer (and unmap frees it ...) - * - * videobuf_free_dmabuf() - * no comment ... - * - */ - -struct videobuf_dmabuf { - /* for userland buffer */ - int offset; - struct page **pages; - - /* for kernel buffers */ - void *vmalloc; - - /* common */ - struct scatterlist *sglist; - int sglen; - int nr_pages; - int direction; -}; - -int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, - unsigned long data, unsigned long size); -int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, - int nr_pages); -int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma); -int videobuf_dma_pci_sync(struct pci_dev *dev, - struct videobuf_dmabuf *dma); -int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma); -int videobuf_dma_free(struct videobuf_dmabuf *dma); - -/* --------------------------------------------------------------------- */ - -/* - * A small set of helper functions to manage video4linux buffers. - * - * struct videobuf_buffer holds the data structures used by the helper - * functions, additionally some commonly used fields for v4l buffers - * (width, height, lists, waitqueue) are in there. That struct should - * be used as first element in the drivers buffer struct. - * - * about the mmap helpers (videobuf_mmap_*): - * - * The mmaper function allows to map any subset of contingous buffers. - * This includes one mmap() call for all buffers (which the original - * video4linux API uses) as well as one mmap() for every single buffer - * (which v4l2 uses). - * - * If there is a valid mapping for a buffer, buffer->baddr/bsize holds - * userspace address + size which can be feeded into the - * videobuf_dma_init_user function listed above. - * - */ - -struct videobuf_buffer; -struct videobuf_queue; - -struct videobuf_mapping { - unsigned int count; - int highmem_ok; - unsigned long start; - unsigned long end; - struct videobuf_queue *q; -}; - -enum videobuf_state { - STATE_NEEDS_INIT = 0, - STATE_PREPARED = 1, - STATE_QUEUED = 2, - STATE_ACTIVE = 3, - STATE_DONE = 4, - STATE_ERROR = 5, - STATE_IDLE = 6, -}; - -struct videobuf_buffer { - unsigned int i; - - /* info about the buffer */ - unsigned int width; - unsigned int height; - unsigned long size; - enum v4l2_field field; - enum videobuf_state state; - struct videobuf_dmabuf dma; - struct list_head stream; /* QBUF/DQBUF list */ - - /* for mmap'ed buffers */ - size_t boff; /* buffer offset (mmap) */ - size_t bsize; /* buffer size */ - unsigned long baddr; /* buffer addr (userland ptr!) */ - struct videobuf_mapping *map; - - /* touched by irq handler */ - struct list_head queue; - wait_queue_head_t done; - unsigned int field_count; - struct timeval ts; -}; - -struct videobuf_queue_ops { - int (*buf_setup)(struct file *file, - unsigned int *count, unsigned int *size); - int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb, - enum v4l2_field field); - void (*buf_queue)(struct file *file,struct videobuf_buffer *vb); - void (*buf_release)(struct file *file,struct videobuf_buffer *vb); -}; - -struct videobuf_queue { - struct semaphore lock; - spinlock_t *irqlock; - struct pci_dev *pci; - - enum v4l2_buf_type type; - unsigned int msize; - enum v4l2_field field; - enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ - struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; - struct videobuf_queue_ops *ops; - - /* capture via mmap() + ioctl(QBUF/DQBUF) */ - unsigned int streaming; - struct list_head stream; - - /* capture via read() */ - unsigned int reading; - unsigned int read_off; - struct videobuf_buffer *read_buf; -}; - -void* videobuf_alloc(unsigned int size); -int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); -int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb); - -void videobuf_queue_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, - struct pci_dev *pci, spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize); -int videobuf_queue_is_busy(struct videobuf_queue *q); -void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q); - -void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, - enum v4l2_buf_type type); -int videobuf_reqbufs(struct file *file, struct videobuf_queue *q, - struct v4l2_requestbuffers *req); -int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); -int videobuf_qbuf(struct file *file, struct videobuf_queue *q, - struct v4l2_buffer *b); -int videobuf_dqbuf(struct file *file, struct videobuf_queue *q, - struct v4l2_buffer *b); -int videobuf_streamon(struct file *file, struct videobuf_queue *q); -int videobuf_streamoff(struct file *file, struct videobuf_queue *q); - -int videobuf_read_start(struct file *file, struct videobuf_queue *q); -void videobuf_read_stop(struct file *file, struct videobuf_queue *q); -ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q, - char *data, size_t count, loff_t *ppos, - int vbihack); -ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q, - char *data, size_t count, loff_t *ppos); -unsigned int videobuf_poll_stream(struct file *file, - struct videobuf_queue *q, - poll_table *wait); - -int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, - unsigned int bcount, unsigned int bsize); -int videobuf_mmap_free(struct file *file, struct videobuf_queue *q); -int videobuf_mmap_mapper(struct vm_area_struct *vma, - struct videobuf_queue *q); - -/* --------------------------------------------------------------------- */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/include/media/audiochip.h b/include/media/audiochip.h new file mode 100644 index 000000000000..e5a5cd2bb172 --- /dev/null +++ b/include/media/audiochip.h @@ -0,0 +1,24 @@ +#ifndef AUDIOCHIP_H +#define AUDIOCHIP_H + +/* ---------------------------------------------------------------------- */ + +/* v4l device was opened in Radio mode */ +#define AUDC_SET_RADIO _IO('m',2) +/* select from TV,radio,extern,MUTE */ +#define AUDC_SET_INPUT _IOW('m',17,int) + +/* audio inputs */ +#define AUDIO_TUNER 0x00 +#define AUDIO_RADIO 0x01 +#define AUDIO_EXTERN 0x02 +#define AUDIO_INTERN 0x03 +#define AUDIO_OFF 0x04 +#define AUDIO_ON 0x05 +#define AUDIO_MUTE 0x80 +#define AUDIO_UNMUTE 0x81 + +/* misc stuff to pass around config info to i2c chips */ +#define AUDC_CONFIG_PINNACLE _IOW('m',32,int) + +#endif /* AUDIOCHIP_H */ diff --git a/include/media/id.h b/include/media/id.h new file mode 100644 index 000000000000..f9360b0665bb --- /dev/null +++ b/include/media/id.h @@ -0,0 +1,35 @@ +/* FIXME: this temporarely, until these are included in linux/i2c-id.h */ + +/* drivers */ +#ifndef I2C_DRIVERID_TVMIXER +# define I2C_DRIVERID_TVMIXER I2C_DRIVERID_EXP0 +#endif +#ifndef I2C_DRIVERID_TVAUDIO +# define I2C_DRIVERID_TVAUDIO I2C_DRIVERID_EXP1 +#endif + +/* chips */ +#ifndef I2C_DRIVERID_DPL3518 +# define I2C_DRIVERID_DPL3518 I2C_DRIVERID_EXP2 +#endif +#ifndef I2C_DRIVERID_TDA9873 +# define I2C_DRIVERID_TDA9873 I2C_DRIVERID_EXP3 +#endif +#ifndef I2C_DRIVERID_TDA9875 +# define I2C_DRIVERID_TDA9875 I2C_DRIVERID_EXP0+4 +#endif +#ifndef I2C_DRIVERID_PIC16C54_PV951 +# define I2C_DRIVERID_PIC16C54_PV951 I2C_DRIVERID_EXP0+5 +#endif +#ifndef I2C_DRIVERID_TDA7432 +# define I2C_DRIVERID_TDA7432 I2C_DRIVERID_EXP0+6 +#endif +#ifndef I2C_DRIVERID_TDA9874 +# define I2C_DRIVERID_TDA9874 I2C_DRIVERID_EXP0+7 +#endif + +/* algorithms */ +#ifndef I2C_ALGO_SAA7134 +# define I2C_ALGO_SAA7134 0x090000 +#endif + diff --git a/include/media/tuner.h b/include/media/tuner.h new file mode 100644 index 000000000000..f716e23dda8e --- /dev/null +++ b/include/media/tuner.h @@ -0,0 +1,94 @@ +/* + tuner.h - definition for different tuners + + Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de) + minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.de) + + 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. +*/ + +#ifndef _TUNER_H +#define _TUNER_H + +#include "id.h" + +#define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ +#define TUNER_PHILIPS_PAL_I 1 +#define TUNER_PHILIPS_NTSC 2 +#define TUNER_PHILIPS_SECAM 3 /* you must actively select B/G, L, L` */ +#define TUNER_ABSENT 4 +#define TUNER_PHILIPS_PAL 5 +#define TUNER_TEMIC_NTSC 6 /* 4032 FY5 (3X 7004, 9498, 9789) */ +#define TUNER_TEMIC_PAL_I 7 /* 4062 FY5 (3X 8501, 9957) */ +#define TUNER_TEMIC_4036FY5_NTSC 8 /* 4036 FY5 (3X 1223, 1981, 7686) */ +#define TUNER_ALPS_TSBH1_NTSC 9 +#define TUNER_ALPS_TSBE1_PAL 10 +#define TUNER_ALPS_TSBB5_PAL_I 11 +#define TUNER_ALPS_TSBE5_PAL 12 +#define TUNER_ALPS_TSBC5_PAL 13 +#define TUNER_TEMIC_4006FH5_PAL 14 /* 4006 FH5 (3X 9500, 9501, 7291) */ +#define TUNER_ALPS_TSHC6_NTSC 15 +#define TUNER_TEMIC_PAL_DK 16 /* 4016 FY5 (3X 1392, 1393) */ +#define TUNER_PHILIPS_NTSC_M 17 +#define TUNER_TEMIC_4066FY5_PAL_I 18 /* 4066 FY5 (3X 7032, 7035) */ +#define TUNER_TEMIC_4006FN5_MULTI_PAL 19 /* B/G, I and D/K autodetected (3X 7595, 7606, 7657)*/ +#define TUNER_TEMIC_4009FR5_PAL 20 /* incl. FM radio (3X 7607, 7488, 7711)*/ +#define TUNER_TEMIC_4039FR5_NTSC 21 /* incl. FM radio (3X 7246, 7578, 7732)*/ +#define TUNER_TEMIC_4046FM5 22 /* you must actively select B/G, D/K, I, L, L` ! (3X 7804, 7806, 8103, 8104)*/ +#define TUNER_PHILIPS_PAL_DK 23 +#define TUNER_PHILIPS_FQ1216ME 24 /* you must actively select B/G/D/K, I, L, L` */ +#define TUNER_LG_PAL_I_FM 25 +#define TUNER_LG_PAL_I 26 +#define TUNER_LG_NTSC_FM 27 +#define TUNER_LG_PAL_FM 28 +#define TUNER_LG_PAL 29 +#define TUNER_TEMIC_4009FN5_MULTI_PAL_FM 30 /* B/G, I and D/K autodetected (3X 8155, 8160, 8163)*/ +#define TUNER_SHARP_2U5JF5540_NTSC 31 +#define TUNER_Samsung_PAL_TCPM9091PD27 32 +#define TUNER_MT2032 33 +#define TUNER_TEMIC_4106FH5 34 /* 4106 FH5 (3X 7808, 7865)*/ +#define TUNER_TEMIC_4012FY5 35 /* 4012 FY5 (3X 0971, 1099)*/ +#define TUNER_TEMIC_4136FY5 36 /* 4136 FY5 (3X 7708, 7746)*/ +#define TUNER_LG_PAL_NEW_TAPC 37 +#define TUNER_PHILIPS_FM1216ME_MK3 38 +#define TUNER_LG_NTSC_NEW_TAPC 39 + + + + +#define NOTUNER 0 +#define PAL 1 /* PAL_BG */ +#define PAL_I 2 +#define NTSC 3 +#define SECAM 4 + +#define NoTuner 0 +#define Philips 1 +#define TEMIC 2 +#define Sony 3 +#define Alps 4 +#define LGINNOTEK 5 +#define SHARP 6 +#define Samsung 7 +#define Microtune 8 + +#define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ +#define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ +#if 0 /* obsolete */ +# define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ +# define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ +#endif + +#endif diff --git a/include/media/video-buf.h b/include/media/video-buf.h new file mode 100644 index 000000000000..fceea3e24f75 --- /dev/null +++ b/include/media/video-buf.h @@ -0,0 +1,234 @@ +/* + * generic helper functions for video4linux capture buffers, to handle + * memory management and PCI DMA. Right now bttv + saa7134 use it. + * + * The functions expect the hardware being able to scatter gatter + * (i.e. the buffers are not linear in physical memory, but fragmented + * into PAGE_SIZE chunks). They also assume the driver does not need + * to touch the video data (thus it is probably not useful for USB as + * data often must be uncompressed by the drivers). + * + * (c) 2001,02 Gerd Knorr + * + * 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. + */ + +#include + +/* --------------------------------------------------------------------- */ + +/* + * Return a scatterlist for some page-aligned vmalloc()'ed memory + * block (NULL on errors). Memory for the scatterlist is allocated + * using kmalloc. The caller must free the memory. + */ +struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); + +/* + * Return a scatterlist for a an array of userpages (NULL on errors). + * Memory for the scatterlist is allocated using kmalloc. The caller + * must free the memory. + */ +struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, + int offset); +int videobuf_lock(struct page **pages, int nr_pages); +int videobuf_unlock(struct page **pages, int nr_pages); + +/* --------------------------------------------------------------------- */ + +/* + * A small set of helper functions to manage buffers (both userland + * and kernel) for DMA. + * + * videobuf_init_*_dmabuf() + * creates a buffer. The userland version takes a userspace + * pointer + length. The kernel version just wants the size and + * does memory allocation too using vmalloc_32(). + * + * videobuf_pci_*_dmabuf() + * see Documentation/DMA-mapping.txt, these functions to + * basically the same. The map function does also build a + * scatterlist for the buffer (and unmap frees it ...) + * + * videobuf_free_dmabuf() + * no comment ... + * + */ + +struct videobuf_dmabuf { + /* for userland buffer */ + int offset; + struct page **pages; + + /* for kernel buffers */ + void *vmalloc; + + /* common */ + struct scatterlist *sglist; + int sglen; + int nr_pages; + int direction; +}; + +int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, + unsigned long data, unsigned long size); +int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, + int nr_pages); +int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma); +int videobuf_dma_pci_sync(struct pci_dev *dev, + struct videobuf_dmabuf *dma); +int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma); +int videobuf_dma_free(struct videobuf_dmabuf *dma); + +/* --------------------------------------------------------------------- */ + +/* + * A small set of helper functions to manage video4linux buffers. + * + * struct videobuf_buffer holds the data structures used by the helper + * functions, additionally some commonly used fields for v4l buffers + * (width, height, lists, waitqueue) are in there. That struct should + * be used as first element in the drivers buffer struct. + * + * about the mmap helpers (videobuf_mmap_*): + * + * The mmaper function allows to map any subset of contingous buffers. + * This includes one mmap() call for all buffers (which the original + * video4linux API uses) as well as one mmap() for every single buffer + * (which v4l2 uses). + * + * If there is a valid mapping for a buffer, buffer->baddr/bsize holds + * userspace address + size which can be feeded into the + * videobuf_dma_init_user function listed above. + * + */ + +struct videobuf_buffer; +struct videobuf_queue; + +struct videobuf_mapping { + unsigned int count; + int highmem_ok; + unsigned long start; + unsigned long end; + struct videobuf_queue *q; +}; + +enum videobuf_state { + STATE_NEEDS_INIT = 0, + STATE_PREPARED = 1, + STATE_QUEUED = 2, + STATE_ACTIVE = 3, + STATE_DONE = 4, + STATE_ERROR = 5, + STATE_IDLE = 6, +}; + +struct videobuf_buffer { + unsigned int i; + + /* info about the buffer */ + unsigned int width; + unsigned int height; + unsigned long size; + enum v4l2_field field; + enum videobuf_state state; + struct videobuf_dmabuf dma; + struct list_head stream; /* QBUF/DQBUF list */ + + /* for mmap'ed buffers */ + size_t boff; /* buffer offset (mmap) */ + size_t bsize; /* buffer size */ + unsigned long baddr; /* buffer addr (userland ptr!) */ + struct videobuf_mapping *map; + + /* touched by irq handler */ + struct list_head queue; + wait_queue_head_t done; + unsigned int field_count; + struct timeval ts; +}; + +struct videobuf_queue_ops { + int (*buf_setup)(struct file *file, + unsigned int *count, unsigned int *size); + int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb, + enum v4l2_field field); + void (*buf_queue)(struct file *file,struct videobuf_buffer *vb); + void (*buf_release)(struct file *file,struct videobuf_buffer *vb); +}; + +struct videobuf_queue { + struct semaphore lock; + spinlock_t *irqlock; + struct pci_dev *pci; + + enum v4l2_buf_type type; + unsigned int msize; + enum v4l2_field field; + enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ + struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; + struct videobuf_queue_ops *ops; + + /* capture via mmap() + ioctl(QBUF/DQBUF) */ + unsigned int streaming; + struct list_head stream; + + /* capture via read() */ + unsigned int reading; + unsigned int read_off; + struct videobuf_buffer *read_buf; +}; + +void* videobuf_alloc(unsigned int size); +int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); +int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb); + +void videobuf_queue_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + struct pci_dev *pci, spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize); +int videobuf_queue_is_busy(struct videobuf_queue *q); +void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q); + +void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, + enum v4l2_buf_type type); +int videobuf_reqbufs(struct file *file, struct videobuf_queue *q, + struct v4l2_requestbuffers *req); +int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); +int videobuf_qbuf(struct file *file, struct videobuf_queue *q, + struct v4l2_buffer *b); +int videobuf_dqbuf(struct file *file, struct videobuf_queue *q, + struct v4l2_buffer *b); +int videobuf_streamon(struct file *file, struct videobuf_queue *q); +int videobuf_streamoff(struct file *file, struct videobuf_queue *q); + +int videobuf_read_start(struct file *file, struct videobuf_queue *q); +void videobuf_read_stop(struct file *file, struct videobuf_queue *q); +ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q, + char *data, size_t count, loff_t *ppos, + int vbihack); +ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q, + char *data, size_t count, loff_t *ppos); +unsigned int videobuf_poll_stream(struct file *file, + struct videobuf_queue *q, + poll_table *wait); + +int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, + unsigned int bcount, unsigned int bsize); +int videobuf_mmap_free(struct file *file, struct videobuf_queue *q); +int videobuf_mmap_mapper(struct vm_area_struct *vma, + struct videobuf_queue *q); + +/* --------------------------------------------------------------------- */ + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ -- cgit v1.2.3 From 0ba2db65cc731f045acd51a16e2ed3786a6292d4 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 12 Mar 2003 10:00:49 +0000 Subject: [SERIAL] Only update the console termios cflag once --- drivers/serial/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/core.c b/drivers/serial/core.c index 18d74e98db4b..cd71c9e65fb0 100644 --- a/drivers/serial/core.c +++ b/drivers/serial/core.c @@ -1400,7 +1400,7 @@ static void uart_update_termios(struct uart_state *state) struct tty_struct *tty = state->info->tty; struct uart_port *port = state->port; - if (uart_console(port)) { + if (uart_console(port) && port->cons->cflag) { tty->termios->c_cflag = port->cons->cflag; port->cons->cflag = 0; } -- cgit v1.2.3 From d25e3ba0452884d6125b349ab45eb5dfb33d4b44 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 18:31:07 -0800 Subject: i2c: add bus driver for ALI15x3 devices This is from the i2c CVS tree. --- drivers/i2c/busses/Kconfig | 14 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ali15x3.c | 587 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 602 insertions(+) create mode 100644 drivers/i2c/busses/i2c-ali15x3.c (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index dcd25b1c59c3..50c824a5bbff 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -5,6 +5,20 @@ menu "I2C Hardware Sensors Mainboard support" +config I2C_ALI15X3 + tristate " ALI 15x3" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the + Acer Labs Inc. (ALI) M1514 and M1543 motherboard I2C interfaces. + + This can also be built as a module. If so, the module will be + called i2c-ali15x3. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + config I2C_AMD756 tristate " AMD 756/766" depends on I2C && I2C_PROC diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 181b1d97e0a9..f6aa23804374 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,5 +2,6 @@ # Makefile for the kernel hardware sensors bus drivers. # +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c new file mode 100644 index 000000000000..24f667243cdd --- /dev/null +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -0,0 +1,587 @@ +/* + ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard and + Philip Edelbrock and + Mark D. Studebaker + + 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. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + + +static void ali15x3_do_pause(unsigned int amount); +static int ali15x3_transaction(void); + +static unsigned short ali15x3_smba = 0; + +/* Detect whether a ALI15X3 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int ali15x3_setup(void) +{ + u16 a; + unsigned char temp; + + struct pci_dev *ALI15X3_dev; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-ali15x3.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the ALI15X3, M7101 device */ + ALI15X3_dev = NULL; + ALI15X3_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, ALI15X3_dev); + if (ALI15X3_dev == NULL) { + printk("i2c-ali15x3.o: Error: Can't detect ali15x3!\n"); + return -ENODEV; + } + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. +*/ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-ali15x3.o: forcing ISA address 0x%04X\n", ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(ALI15X3_dev, SMBBA, &a)) + return -ENODEV; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + printk("i2c-ali15x3.o: force address failed - not supported?\n"); + return -ENODEV; + } + } +/* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba); +#endif /* DEBUG */ + + return 0; +} + + +/* Internally used pause function */ +void ali15x3_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali15x3_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI15X3_ABORT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + if (temp & ALI15X3_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali15x3.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali15x3.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali15x3.o: SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -1; +#ifdef DEBUG + printk("i2c-ali15x3.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't + do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI15X3_STS_COLL) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -1; + printk("i2c-ali15x3.o: Error: device error\n"); + } +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + +/* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + if (ali15x3_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } + return 0; +} + + +u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = ali15x3_access, + .functionality = ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id ali15x3_ids[] __devinitdata = { + { 0, } +}; + +static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + if (ali15x3_setup()) { + printk + ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); + + return -ENODEV; + } + + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", + ali15x3_smba); + i2c_add_adapter(&ali15x3_adapter); +} + +static void __devexit ali15x3_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&ali15x3_adapter); +} + +static struct pci_driver ali15x3_driver = { + .name = "ali15x3 smbus", + .id_table = ali15x3_ids, + .probe = ali15x3_probe, + .remove = __devexit_p(ali15x3_remove), +}; + +static int __init i2c_ali15x3_init(void) +{ + printk("i2c-ali15x3.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&ali15x3_driver); +} + + +static void __exit i2c_ali15x3_exit(void) +{ + pci_unregister_driver(&ali15x3_driver); + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_ali15x3_init); +module_exit(i2c_ali15x3_exit); -- cgit v1.2.3 From e1700574e056b8bc58f23af2cb0b3587f9fb4a02 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 18:50:41 -0800 Subject: i2c: get i2c-ali15x3 driver to actually bind to a PCI device. --- drivers/i2c/busses/i2c-ali15x3.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 24f667243cdd..aeb4f55ac54d 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -135,32 +135,11 @@ static int ali15x3_transaction(void); static unsigned short ali15x3_smba = 0; -/* Detect whether a ALI15X3 can be found, and initialize it, where necessary. - Note the differences between kernels with the old PCI BIOS interface and - newer kernels with the real PCI interface. In compat.h some things are - defined to make the transition easier. */ -int ali15x3_setup(void) +int ali15x3_setup(struct pci_dev *ALI15X3_dev) { u16 a; unsigned char temp; - struct pci_dev *ALI15X3_dev; - - /* First check whether we can access PCI at all */ - if (pci_present() == 0) { - printk("i2c-ali15x3.o: Error: No PCI-bus found!\n"); - return -ENODEV; - } - - /* Look for the ALI15X3, M7101 device */ - ALI15X3_dev = NULL; - ALI15X3_dev = pci_find_device(PCI_VENDOR_ID_AL, - PCI_DEVICE_ID_AL_M7101, ALI15X3_dev); - if (ALI15X3_dev == NULL) { - printk("i2c-ali15x3.o: Error: Can't detect ali15x3!\n"); - return -ENODEV; - } - /* Check the following things: - SMB I/O address is initialized - Device is enabled @@ -534,12 +513,18 @@ static struct i2c_adapter ali15x3_adapter = { static struct pci_device_id ali15x3_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_AL, + .device = PCI_DEVICE_ID_AL_M7101, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { 0, } }; static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id) { - if (ali15x3_setup()) { + if (ali15x3_setup(dev)) { printk ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); @@ -549,6 +534,7 @@ static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_ sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", ali15x3_smba); i2c_add_adapter(&ali15x3_adapter); + return 0; } static void __devexit ali15x3_remove(struct pci_dev *dev) -- cgit v1.2.3 From fb72dee2fca7586e0885c5faa650cfa7b28d62e7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 19:15:15 -0800 Subject: i2c: add bus driver for Intel 801 devices This is from the i2c CVS tree. --- drivers/i2c/busses/Kconfig | 23 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-i801.c | 710 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 734 insertions(+) create mode 100644 drivers/i2c/busses/i2c-i801.c (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 50c824a5bbff..604c312137ce 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -53,5 +53,28 @@ config I2C_AMD8111 in the lm_sensors package, which you can download at http://www.lm-sensors.nu +config I2C_I801 + tristate " Intel 801" + depends on I2C && I2C_PROC + help + If you say yes to this option, support will be included for the Intel + 801 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + 82801AA + 82801AB + 82801BA + 82801CA/CAM + 82801DB + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read . + + The module will be called i2c-i801. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index f6aa23804374..482e2c69700f 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o +obj-$(CONFIG_I2C_I801) += i2c-i801.o diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c new file mode 100644 index 000000000000..e7bbf0bc5a91 --- /dev/null +++ b/drivers/i2c/busses/i2c-i801.c @@ -0,0 +1,710 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard , + Philip Edelbrock , and Mark D. Studebaker + + + 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. +*/ + +/* + SUPPORTED DEVICES PCI ID + 82801AA 2413 + 82801AB 2423 + 82801BA 2443 + 82801CA/CAM 2483 + 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) + + This driver supports several versions of Intel's I/O Controller Hubs (ICH). + For SMBus support, they are similar to the PIIX4 and are part + of Intel's '810' and other chipsets. + See the doc/busses/i2c-i801 file for details. + I2C Block Read and Process Call are not supported. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +/* #define DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); + +#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC +#define HAVE_PEC +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801CA_SMBUS +#define PCI_DEVICE_ID_INTEL_82801CA_SMBUS 0x2483 +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801DB_SMBUS +#define PCI_DEVICE_ID_INTEL_82801DB_SMBUS 0x24C3 +#endif + +static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3, + PCI_DEVICE_ID_INTEL_82801AB_3, + PCI_DEVICE_ID_INTEL_82801BA_2, + PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + 0 }; + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) +#define SMBPEC (8 + i801_smba) /* ICH4 only */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ + +/* PCI Address Constants */ +#define SMBBA 0x020 +#define SMBHSTCFG 0x040 +#define SMBREV 0x008 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Other settings */ +#define MAX_TIMEOUT 100 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH4 only */ + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the I801 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the I801 at the given address. " + "EXTREMELY DANGEROUS!"); + + + + + +static void i801_do_pause(unsigned int amount); +static int i801_transaction(void); +static int i801_block_transaction(union i2c_smbus_data *data, + char read_write, int command); + + + + +static unsigned short i801_smba = 0; +static struct pci_dev *I801_dev = NULL; +static int isich4 = 0; + +/* Detect whether a I801 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int i801_setup(void) +{ + int error_return = 0; + int *num = supported; + unsigned char temp; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk(KERN_WARNING "i2c-i801.o: Error: No PCI-bus found!\n"); + error_return = -ENODEV; + goto END; + } + + /* Look for each chip */ + /* Note: we keep on searching until we have found 'function 3' */ + I801_dev = NULL; + do { + if((I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + *num, I801_dev))) { + if(PCI_FUNC(I801_dev->devfn) != 3) + continue; + break; + } + num++; + } while (*num != 0); + + if (I801_dev == NULL) { + printk + (KERN_WARNING "i2c-i801.o: Error: Can't detect I801, function 3!\n"); + error_return = -ENODEV; + goto END; + } + isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS; + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + if(i801_smba == 0) { + printk(KERN_ERR "i2c-i801.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(i801_smba, (isich4 ? 16 : 8))) { + printk + (KERN_ERR "i2c-i801.o: I801_smb region 0x%x already in use!\n", + i801_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + pci_write_config_byte(I801_dev, SMBHSTCFG, temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the device first. */ + if (force_addr) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(I801_dev, SMBBA, i801_smba); + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_WARNING "i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); + printk(KERN_WARNING "i2c-i801.o: enabling SMBus device\n"); + } + + request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + (KERN_DEBUG "i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_DEBUG "i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-i801.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Successfull!\n"); +#endif + } + } + + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + i801_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-i801.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* All-inclusive block transaction function */ +int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int command) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout; + unsigned char hostc, errmask; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + printk("i2c-i801.o: " + "I2C_SMBUS_I2C_BLOCK_READ not supported!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + outb_p(data->block[1], SMBBLKDAT); + } else { + len = 32; /* max for reads */ + } + + if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { + /* set 32 byte buffer */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; +#if 0 /* now using HW PEC */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) + smbcmd |= I801_PEC_EN; +#endif + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Block (pre %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb_p(SMBHSTSTS); + if (i == 1) { + /* Erronenous conditions before transaction: + * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + errmask=0x9f; + } else { + /* Erronenous conditions during transaction: + * Failed, Bus_Err, Dev_Err, Intr */ + errmask=0x1e; + } + if (temp & errmask) { +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + (KERN_ERR "i2c-i801.o: Reset failed! (%02x)\n", + temp); + result = -1; + goto END; + } + if (i != 1) { + result = -1; /* if die in middle of block transaction, fail */ + goto END; + } + } + + if (i == 1) { +#if 0 /* #ifdef HAVE_PEC (now using HW PEC) */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + if(read_write == I2C_SMBUS_WRITE) + outb_p(data->block[len + 1], SMBPEC); + } +#endif + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + } + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ((!(temp & 0x80)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk(KERN_ERR "i2c-i801.o: Bus collision!\n"); + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: no response!\n"); +#endif + } + + if (i == 1 && read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1) + len = 1; + if (len > 32) + len = 32; + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT); + if ((temp & 0x9e) != 0x00) + outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ + +#ifdef DEBUG + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { + printk + (KERN_DEBUG "i2c-i801.o: Bad status (%02x) at end of transaction\n", + temp); + } + printk + (KERN_DEBUG "i2c-i801.o: Block (post %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) + goto END; + } + +#ifdef HAVE_PEC + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + /* wait for INTR bit as advised by Intel */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } while ((!(temp & 0x02)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout >= MAX_TIMEOUT) { + printk(KERN_DEBUG "i2c-i801.o: PEC Timeout!\n"); + } +#if 0 /* now using HW PEC */ + if(read_write == I2C_SMBUS_READ) { + data->block[len + 1] = inb_p(SMBPEC); + } +#endif + outb_p(temp, SMBHSTSTS); + } +#endif + result = 0; +END: + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + int hwpec = 0; + int block = 0; + int ret, xact = 0; + +#ifdef HAVE_PEC + if(isich4) + hwpec = (flags & I2C_CLIENT_PEC) != 0; +#endif + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + xact = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + xact = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + xact = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: +#ifdef HAVE_PEC + case I2C_SMBUS_BLOCK_DATA_PEC: + if(hwpec && size == I2C_SMBUS_BLOCK_DATA) + size = I2C_SMBUS_BLOCK_DATA_PEC; +#endif + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + block = 1; + break; + case I2C_SMBUS_PROC_CALL: + default: + printk(KERN_ERR "i2c-i801.o: Unsupported transaction %d\n", size); + return -1; + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(1, SMBAUXCTL); /* enable HW PEC */ + } +#endif + if(block) + ret = i801_block_transaction(data, read_write, size); + else { + outb_p(xact | ENABLE_INT9, SMBHSTCNT); + ret = i801_transaction(); + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(0, SMBAUXCTL); + } +#endif + + if(block) + return ret; + if(ret) + return -1; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + return 0; + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + } + return 0; +} + + +u32 i801_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK +#ifdef HAVE_PEC + | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC | + I2C_FUNC_SMBUS_HWPEC_CALC + : 0) +#endif + ; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = i801_access, + .functionality = i801_func, +}; + +static struct i2c_adapter i801_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id i801_ids[] __devinitdata = { + { 0, } +}; + +static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + + if (i801_setup()) { + printk + (KERN_WARNING "i2c-i801.o: I801 not detected, module not inserted.\n"); + return -ENODEV; + } + + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", + i801_smba); + i2c_add_adapter(&i801_adapter); +} + +static void __devexit i801_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&i801_adapter); +} + +static struct pci_driver i801_driver = { + .name = "i801 smbus", + .id_table = i801_ids, + .probe = i801_probe, + .remove = __devexit_p(i801_remove), +}; + +static int __init i2c_i801_init(void) +{ + printk(KERN_INFO "i2c-i801.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&i801_driver); +} + + +static void __exit i2c_i801_exit(void) +{ + pci_unregister_driver(&i801_driver); + release_region(i801_smba, (isich4 ? 16 : 8)); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I801 SMBus driver"); + +module_init(i2c_i801_init); +module_exit(i2c_i801_exit); -- cgit v1.2.3 From 07983061cf5ba2b84e9001c24d2f2a7dd0d7e708 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 19:38:03 -0800 Subject: i2c: get i2c-i801 driver to actually bind to a PCI device. --- drivers/i2c/busses/i2c-i801.c | 74 ++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index e7bbf0bc5a91..6ed514699594 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -128,49 +128,21 @@ static int i801_transaction(void); static int i801_block_transaction(union i2c_smbus_data *data, char read_write, int command); +static unsigned short i801_smba; +static struct pci_dev *I801_dev; +static int isich4; - - -static unsigned short i801_smba = 0; -static struct pci_dev *I801_dev = NULL; -static int isich4 = 0; - -/* Detect whether a I801 can be found, and initialize it, where necessary. - Note the differences between kernels with the old PCI BIOS interface and - newer kernels with the real PCI interface. In compat.h some things are - defined to make the transition easier. */ -int i801_setup(void) +static int i801_setup(struct pci_dev *dev) { int error_return = 0; int *num = supported; unsigned char temp; - /* First check whether we can access PCI at all */ - if (pci_present() == 0) { - printk(KERN_WARNING "i2c-i801.o: Error: No PCI-bus found!\n"); - error_return = -ENODEV; - goto END; - } - - /* Look for each chip */ /* Note: we keep on searching until we have found 'function 3' */ - I801_dev = NULL; - do { - if((I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - *num, I801_dev))) { - if(PCI_FUNC(I801_dev->devfn) != 3) - continue; - break; - } - num++; - } while (*num != 0); + if(PCI_FUNC(dev->devfn) != 3) + return -ENODEV; - if (I801_dev == NULL) { - printk - (KERN_WARNING "i2c-i801.o: Error: Can't detect I801, function 3!\n"); - error_return = -ENODEV; - goto END; - } + I801_dev = dev; isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS; /* Determine the address of the SMBus areas */ @@ -658,13 +630,43 @@ static struct i2c_adapter i801_adapter = { static struct pci_device_id i801_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AA_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801BA_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82801DB_SMBUS, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, { 0, } }; static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id) { - if (i801_setup()) { + if (i801_setup(dev)) { printk (KERN_WARNING "i2c-i801.o: I801 not detected, module not inserted.\n"); return -ENODEV; -- cgit v1.2.3 From 66ba06f125452a62d7278019b12671bd3309ca8c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 19:52:26 -0800 Subject: i2c: add bus driver for Intel PIIX4 devices This is from the i2c CVS tree. --- drivers/i2c/busses/Kconfig | 25 +- drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-piix4.c | 529 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 drivers/i2c/busses/i2c-piix4.c (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 604c312137ce..67abe170a8de 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -55,7 +55,7 @@ config I2C_AMD8111 config I2C_I801 tristate " Intel 801" - depends on I2C && I2C_PROC + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL help If you say yes to this option, support will be included for the Intel 801 family of mainboard I2C interfaces. Specifically, the following @@ -76,5 +76,28 @@ config I2C_I801 in the lm_sensors package, which you can download at http://www.lm-sensors.nu +config I2C_PIIX4 + tristate " Intel PIIX4" + depends on I2C && I2C_PROC && PCI && EXPERIMENTAL + help + If you say yes to this option, support will be included for the Intel + PIIX4 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset is supported: + Intel PIIX4 + Intel 440MX + Serverworks OSB4 + Serverworks CSB5 + SMSC Victory66 + + This can also be built as a module which can be inserted and removed + while the kernel is running. If you want to compile it as a module, + say M here and read . + + The module will be called i2c-piix4. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 482e2c69700f..c8e61d4a6f88 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c new file mode 100644 index 000000000000..28d2d50113ff --- /dev/null +++ b/drivers/i2c/busses/i2c-piix4.c @@ -0,0 +1,529 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 Frodo Looijaard and + Philip Edelbrock + + 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. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +/* Note: We assume all devices are identical + to the Intel PIIX4; we only mention it during detection. */ + +static struct sd supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, 3, "PIIX4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, 0, "OSB4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, 0, "CSB5"}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, 3, "440MX"}, + {PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3, 0, "Victory66"}, + {0, 0, 0, NULL} +}; + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +static void piix4_do_pause(unsigned int amount); +static int piix4_transaction(void); + + +static unsigned short piix4_smba = 0; + +#ifdef CONFIG_X86 +/* + * Get DMI information. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,34) +void dmi_scan_mach(void); +#endif + +static int __init ibm_dmi_probe(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34) + extern int is_unsafe_smbus; + return is_unsafe_smbus; +#else +#define IBM_SIGNATURE "IBM" + dmi_scan_mach(); + if(dmi_ident[DMI_SYS_VENDOR] == NULL) + return 0; + if(strncmp(dmi_ident[DMI_SYS_VENDOR], IBM_SIGNATURE, + strlen(IBM_SIGNATURE)) == 0) + return 1; + return 0; +#endif +} +#endif + +/* Detect whether a PIIX4 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int piix4_setup(void) +{ + int error_return = 0; + unsigned char temp; + struct sd *num = supported; + struct pci_dev *PIIX4_dev = NULL; + + if (pci_present() == 0) { + error_return = -ENODEV; + goto END; + } + + /* Look for a supported device/function */ + do { + if((PIIX4_dev = pci_find_device(num->mfr, num->dev, + PIIX4_dev))) { + if(PCI_FUNC(PIIX4_dev->devfn) != num->fn) + continue; + break; + } + PIIX4_dev = NULL; + num++; + } while (num->mfr); + + if (PIIX4_dev == NULL) { + printk + (KERN_ERR "i2c-piix4.o: Error: Can't detect PIIX4 or compatible device!\n"); + error_return = -ENODEV; + goto END; + } + printk(KERN_INFO "i2c-piix4.o: Found %s device\n", num->name); + +#ifdef CONFIG_X86 + if(ibm_dmi_probe()) { + printk + (KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module may corrupt\n"); + printk + (KERN_ERR " your serial eeprom! Refusing to load module!\n"); + error_return = -EPERM; + goto END; + } +#endif + +/* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + printk(KERN_ERR "i2c-piix4.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(piix4_smba, 8)) { + printk + (KERN_ERR "i2c-piix4.o: SMB region 0x%x already in use!\n", + piix4_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to new " + "address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + printk + (KERN_NOTICE "i2c-piix4.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + (KERN_ERR "i2c-piix4.o: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(piix4_smba, 8, "piix4-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void piix4_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + do { + piix4_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + +#ifdef DEBUG + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n"); + result = -1; + } +#endif + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + +#ifdef DEBUG + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + printk + (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n", + temp); + } + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. */ +s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + (KERN_ERR "i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (piix4_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + + +u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = piix4_access, + .functionality = piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + .algo = &smbus_algorithm, +}; + + + +static struct pci_device_id piix4_ids[] __devinitdata = { + { 0, } +}; + +static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", + piix4_smba); + + i2c_add_adapter(&piix4_adapter); +} + +static void __devexit piix4_remove(struct pci_dev *dev) +{ + i2c_del_adapter(&piix4_adapter); +} + + +static struct pci_driver piix4_driver = { + .name = "piix4 smbus", + .id_table = piix4_ids, + .probe = piix4_probe, + .remove = __devexit_p(piix4_remove), +}; + +static int __init i2c_piix4_init(void) +{ + printk("i2c-piix4.o version %s (%s)\n", I2C_VERSION, I2C_DATE); + return pci_module_init(&piix4_driver); +} + + +static void __exit i2c_piix4_exit(void) +{ + pci_unregister_driver(&piix4_driver); + release_region(piix4_smba, 8); +} + + + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); +MODULE_LICENSE("GPL"); + +module_init(i2c_piix4_init); +module_exit(i2c_piix4_exit); -- cgit v1.2.3 From 48ff60a2aecfa6d59d66119fd4a0335b39663c02 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 19:59:13 -0800 Subject: i2c: get i2c-piix4 driver to actually bind to a PCI device. --- drivers/i2c/busses/i2c-piix4.c | 99 ++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 47 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 28d2d50113ff..7121e2b27e45 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -49,18 +49,6 @@ struct sd { const char *name; }; -/* Note: We assume all devices are identical - to the Intel PIIX4; we only mention it during detection. */ - -static struct sd supported[] = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, 3, "PIIX4"}, - {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, 0, "OSB4"}, - {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, 0, "CSB5"}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, 3, "440MX"}, - {PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3, 0, "Victory66"}, - {0, 0, 0, NULL} -}; - /* PIIX4 SMBus address offsets */ #define SMBHSTSTS (0 + piix4_smba) #define SMBHSLVSTS (1 + piix4_smba) @@ -142,41 +130,16 @@ static int __init ibm_dmi_probe(void) } #endif -/* Detect whether a PIIX4 can be found, and initialize it, where necessary. - Note the differences between kernels with the old PCI BIOS interface and - newer kernels with the real PCI interface. In compat.h some things are - defined to make the transition easier. */ -int piix4_setup(void) +static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { int error_return = 0; unsigned char temp; - struct sd *num = supported; - struct pci_dev *PIIX4_dev = NULL; - - if (pci_present() == 0) { - error_return = -ENODEV; - goto END; - } - /* Look for a supported device/function */ - do { - if((PIIX4_dev = pci_find_device(num->mfr, num->dev, - PIIX4_dev))) { - if(PCI_FUNC(PIIX4_dev->devfn) != num->fn) - continue; - break; - } - PIIX4_dev = NULL; - num++; - } while (num->mfr); + /* match up the function */ + if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data) + return -ENODEV; - if (PIIX4_dev == NULL) { - printk - (KERN_ERR "i2c-piix4.o: Error: Can't detect PIIX4 or compatible device!\n"); - error_return = -ENODEV; - goto END; - } - printk(KERN_INFO "i2c-piix4.o: Found %s device\n", num->name); + printk(KERN_INFO "i2c-piix4.o: Found %s device\n", PIIX4_dev->dev.name); #ifdef CONFIG_X86 if(ibm_dmi_probe()) { @@ -267,14 +230,14 @@ int piix4_setup(void) /* Internally used pause function */ -void piix4_do_pause(unsigned int amount) +static void piix4_do_pause(unsigned int amount) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(amount); } /* Another internally used function */ -int piix4_transaction(void) +static int piix4_transaction(void) { int temp; int result = 0; @@ -363,7 +326,7 @@ int piix4_transaction(void) } /* Return -1 on error. */ -s32 piix4_access(struct i2c_adapter * adap, u16 addr, +static s32 piix4_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data) { @@ -456,7 +419,7 @@ s32 piix4_access(struct i2c_adapter * adap, u16 addr, } -u32 piix4_func(struct i2c_adapter *adapter) +static u32 piix4_func(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | @@ -480,16 +443,58 @@ static struct i2c_adapter piix4_adapter = { static struct pci_device_id piix4_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82371AB_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3 + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_OSB4, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_SERVERWORKS, + .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_82443MX_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 3, + }, + { + .vendor = PCI_VENDOR_ID_EFAR, + .device = PCI_DEVICE_ID_EFAR_SLC90E66_3, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = 0, + }, { 0, } }; static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) { + int retval; + + retval = piix4_setup(dev, id); + if (retval) + return retval; sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", piix4_smba); - i2c_add_adapter(&piix4_adapter); + retval = i2c_add_adapter(&piix4_adapter); + + return retval; } static void __devexit piix4_remove(struct pci_dev *dev) -- cgit v1.2.3 From 1464d8c7d4c095cd21f745ef3c07ea71a75b7fd4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 20:16:52 -0800 Subject: i2c: i2c-piix4.c: Clean up the ibm dma scan logic Also export the is_unsafe_smbus variable, which is needed. --- arch/i386/kernel/dmi_scan.c | 3 +++ drivers/i2c/busses/i2c-piix4.c | 20 ++------------------ 2 files changed, 5 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index 179579a1df20..6f755378def8 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -893,3 +894,5 @@ void __init dmi_scan_machine(void) if(err == 0) dmi_check_blacklist(); } + +EXPORT_SYMBOL(is_unsafe_smbus); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 7121e2b27e45..76f9ead0b0a8 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -28,7 +28,6 @@ Note: we assume there can only be one device, with one SMBus interface. */ -#include #include #include #include @@ -104,31 +103,18 @@ static int piix4_transaction(void); static unsigned short piix4_smba = 0; -#ifdef CONFIG_X86 /* * Get DMI information. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,34) -void dmi_scan_mach(void); -#endif - -static int __init ibm_dmi_probe(void) +static int ibm_dmi_probe(void) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34) +#ifdef CONFIG_X86 extern int is_unsafe_smbus; return is_unsafe_smbus; #else -#define IBM_SIGNATURE "IBM" - dmi_scan_mach(); - if(dmi_ident[DMI_SYS_VENDOR] == NULL) - return 0; - if(strncmp(dmi_ident[DMI_SYS_VENDOR], IBM_SIGNATURE, - strlen(IBM_SIGNATURE)) == 0) - return 1; return 0; #endif } -#endif static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { @@ -141,7 +127,6 @@ static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id printk(KERN_INFO "i2c-piix4.o: Found %s device\n", PIIX4_dev->dev.name); -#ifdef CONFIG_X86 if(ibm_dmi_probe()) { printk (KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module may corrupt\n"); @@ -150,7 +135,6 @@ static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id error_return = -EPERM; goto END; } -#endif /* Determine the address of the SMBus areas */ if (force_addr) { -- cgit v1.2.3 From 500d525042b8b4f2df766c385380f8a8d5c4920e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 12 Mar 2003 20:26:39 -0800 Subject: i2c: add i2c sysfs bus support. --- drivers/i2c/i2c-core.c | 33 ++++++++++++++++++++++++++++++--- include/linux/i2c.h | 3 +++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 238c5a31e4f3..6165e88a660c 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -598,10 +598,37 @@ static void __exit i2cproc_cleanup(void) { remove_proc_entry("i2c",proc_bus); } +#else +static int __init i2cproc_init(void) { return 0; } +static void __exit i2cproc_cleanup(void) { } +#endif /* CONFIG_PROC_FS */ + +/* match always succeeds, as we want the probe() to tell if we really accept this match */ +static int i2c_device_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +struct bus_type i2c_bus_type = { + .name = "i2c", + .match = i2c_device_match, +}; + + +static int __init i2c_init(void) +{ + bus_register(&i2c_bus_type); + return i2cproc_init(); +} + +static void __exit i2c_exit(void) +{ + i2cproc_cleanup(); + bus_unregister(&i2c_bus_type); +} -module_init(i2cproc_init); -module_exit(i2cproc_cleanup); -#endif /* def CONFIG_PROC_FS */ +module_init(i2c_init); +module_exit(i2c_exit); /* ---------------------------------------------------- * the functional interface to the i2c busses. diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 0b293a405341..8f86852b16d8 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -34,6 +34,7 @@ #include #include #include +#include /* for struct device */ #include /* --- General options ------------------------------------------------ */ @@ -144,6 +145,8 @@ struct i2c_driver { int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); }; +extern struct bus_type i2c_bus_type; + /* * i2c_client identifies a single device (i.e. chip) that is connected to an * i2c bus. The behaviour is defined by the routines of the driver. This -- cgit v1.2.3 From a693405459827ef869c41467e9460afd174a396f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Mar 2003 00:37:27 -0800 Subject: driver core: Export the legacy_bus structure for drivers to use. --- drivers/base/platform.c | 3 ++- include/linux/device.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index de932ddea39b..b79c1d345192 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -9,7 +9,7 @@ #include #include -static struct device legacy_bus = { +struct device legacy_bus = { .name = "legacy bus", .bus_id = "legacy", }; @@ -75,5 +75,6 @@ int __init platform_bus_init(void) return bus_register(&platform_bus_type); } +EXPORT_SYMBOL(legacy_bus); EXPORT_SYMBOL(platform_device_register); EXPORT_SYMBOL(platform_device_unregister); diff --git a/include/linux/device.h b/include/linux/device.h index e27039ec2222..908d6b708fb7 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -366,6 +366,7 @@ extern int platform_device_register(struct platform_device *); extern void platform_device_unregister(struct platform_device *); extern struct bus_type platform_bus_type; +extern struct device legacy_bus; /* drivers/base/power.c */ extern int device_suspend(u32 state, u32 level); -- cgit v1.2.3 From 820783a6d4fc0e401cd193521089f61fb693fad1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Mar 2003 00:39:40 -0800 Subject: i2c: add driver model support to i2c adapter drivers --- drivers/i2c/busses/i2c-ali15x3.c | 6 ++++-- drivers/i2c/busses/i2c-amd756.c | 3 +++ drivers/i2c/busses/i2c-amd8111.c | 5 ++++- drivers/i2c/busses/i2c-i801.c | 5 ++++- drivers/i2c/busses/i2c-piix4.c | 3 +++ drivers/i2c/i2c-core.c | 13 +++++++++++++ include/linux/i2c.h | 2 ++ 7 files changed, 33 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index aeb4f55ac54d..8d7f372e109f 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -531,10 +531,12 @@ static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_ return -ENODEV; } + /* set up the driverfs linkage to our parent device */ + ali15x3_adapter.dev.parent = &dev->dev; + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", ali15x3_smba); - i2c_add_adapter(&ali15x3_adapter); - return 0; + return i2c_add_adapter(&ali15x3_adapter); } static void __devexit ali15x3_remove(struct pci_dev *dev) diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index b2627572924e..d4bc60ebac3d 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -375,6 +375,9 @@ static int __devinit amd756_probe(struct pci_dev *pdev, printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport); #endif + /* set up the driverfs linkage to our parent device */ + amd756_adapter.dev.parent = &pdev->dev; + sprintf(amd756_adapter.name, "SMBus AMD75x adapter at %04x", amd756_ioport); diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index a3adbe770f8c..07f22e6e8495 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -363,6 +363,9 @@ static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_ smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; + /* set up the driverfs linkage to our parent device */ + smbus->adapter.dev.parent = &dev->dev; + error = i2c_add_adapter(&smbus->adapter); if (error) goto out_release_region; @@ -389,7 +392,7 @@ static void __devexit amd8111_remove(struct pci_dev *dev) } static struct pci_driver amd8111_driver = { - .name = "amd8111 smbus 2.0", + .name = "amd8111 smbus", .id_table = amd8111_ids, .probe = amd8111_probe, .remove = __devexit_p(amd8111_remove), diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 6ed514699594..b1556e08ce54 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -672,9 +672,12 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id return -ENODEV; } + /* set up the driverfs linkage to our parent device */ + i801_adapter.dev.parent = &dev->dev; + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", i801_smba); - i2c_add_adapter(&i801_adapter); + return i2c_add_adapter(&i801_adapter); } static void __devexit i801_remove(struct pci_dev *dev) diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 76f9ead0b0a8..5acd8211b1d6 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -473,6 +473,9 @@ static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id if (retval) return retval; + /* set up the driverfs linkage to our parent device */ + piix4_adapter.dev.parent = &dev->dev; + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", piix4_smba); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 6165e88a660c..004e8e8ef671 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -87,6 +87,16 @@ int i2c_add_adapter(struct i2c_adapter *adap) init_MUTEX(&adap->bus); init_MUTEX(&adap->list); + /* Add the adapter to the driver core. + * If the parent pointer is not set up, + * we add this adapter to the legacy bus. + */ + if (adap->dev.parent == NULL) + adap->dev.parent = &legacy_bus; + sprintf(adap->dev.bus_id, "i2c-%d", i); + strcpy(adap->dev.name, "i2c controller"); + device_register(&adap->dev); + /* inform drivers of new adapters */ for (j=0;jdev); + adapters[i] = NULL; DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name)); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 8f86852b16d8..a8f482b94ba4 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -231,12 +231,14 @@ struct i2c_adapter { int timeout; int retries; + struct device dev; /* the adapter device */ #ifdef CONFIG_PROC_FS /* No need to set this when you initialize the adapter */ int inode; #endif /* def CONFIG_PROC_FS */ }; +#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) /*flags for the driver struct: */ #define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ -- cgit v1.2.3 From 8022d07195c322ce4305d1c09e3360f173513af1 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 13 Mar 2003 08:20:33 -0500 Subject: [hw_random] shuffle files in preparation for hw_random driver update Delete drivers/char/i810_rng.c, superceded. Rename Doc/i810_rng.txt to Doc/hw_random.txt. Rename drv/char/amd768_rng.c to drv/char/hw_random.c. --- Documentation/hw_random.txt | 134 +++++++++++++++ Documentation/i810_rng.txt | 134 --------------- drivers/char/amd768_rng.c | 295 -------------------------------- drivers/char/hw_random.c | 295 ++++++++++++++++++++++++++++++++ drivers/char/i810_rng.c | 404 -------------------------------------------- 5 files changed, 429 insertions(+), 833 deletions(-) create mode 100644 Documentation/hw_random.txt delete mode 100644 Documentation/i810_rng.txt delete mode 100644 drivers/char/amd768_rng.c create mode 100644 drivers/char/hw_random.c delete mode 100644 drivers/char/i810_rng.c (limited to 'drivers') diff --git a/Documentation/hw_random.txt b/Documentation/hw_random.txt new file mode 100644 index 000000000000..21992f820dd4 --- /dev/null +++ b/Documentation/hw_random.txt @@ -0,0 +1,134 @@ + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik + Copyright 2000,2001 Philipp Rumpf + +Introduction: + + The i810_rng device driver is software that makes use of a + special hardware feature on the Intel i8xx-based chipsets, + a Random Number Generator (RNG). + + In order to make effective use of this device driver, you + should download the support software as well. Download the + latest version of the "intel-rng-tools" package from the + i810_rng driver's official Web site: + + http://sourceforge.net/projects/gkernel/ + +About the Intel RNG hardware, from the firmware hub datasheet: + + The Firmware Hub integrates a Random Number Generator (RNG) + using thermal noise generated from inherently random quantum + mechanical properties of silicon. When not generating new random + bits the RNG circuitry will enter a low power state. Intel will + provide a binary software driver to give third party software + access to our RNG for use as a security feature. At this time, + the RNG is only to be used with a system in an OS-present state. + +Theory of operation: + + Character driver. Using the standard open() + and read() system calls, you can read random data from + the i810 RNG device. This data is NOT CHECKED by any + fitness tests, and could potentially be bogus (if the + hardware is faulty or has been tampered with). Data is only + output if the hardware "has-data" flag is set, but nevertheless + a security-conscious person would run fitness tests on the + data before assuming it is truly random. + + /dev/intel_rng is char device major 10, minor 183. + +Driver notes: + + * FIXME: support poll(2) + + NOTE: request_mem_region was removed, for two reasons: + 1) Only one RNG is supported by this driver, 2) The location + used by the RNG is a fixed location in MMIO-addressable memory, + 3) users with properly working BIOS e820 handling will always + have the region in which the RNG is located reserved, so + request_mem_region calls always fail for proper setups. + However, for people who use mem=XX, BIOS e820 information is + -not- in /proc/iomem, and request_mem_region(RNG_ADDR) can + succeed. + +Driver details: + + Based on: + Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet + May 1999 Order Number: 290658-002 R + + Intel 82802 Firmware Hub: Random Number Generator + Programmer's Reference Manual + December 1999 Order Number: 298029-001 R + + Intel 82802 Firmware HUB Random Number Generator Driver + Copyright (c) 2000 Matt Sottek + + Special thanks to Matt Sottek. I did the "guts", he + did the "brains" and all the testing. + +Change history: + + Version 0.9.8: + * Support other i8xx chipsets by adding 82801E detection + * 82801DB detection is the same as for 82801CA. + + Version 0.9.7: + * Support other i8xx chipsets too (by adding 82801BA(M) and + 82801CA(M) detection) + + Version 0.9.6: + * Internal driver cleanups, prep for 1.0.0 release. + + Version 0.9.5: + * Rip out entropy injection via timer. It never ever worked, + and a better solution (rngd) is now available. + + Version 0.9.4: + * Fix: Remove request_mem_region + * Fix: Horrible bugs in FIPS calculation and test execution + + Version 0.9.3: + * Clean up rng_read a bit. + * Update i810_rng driver Web site URL. + * Increase default timer interval to 4 samples per second. + * Abort if mem region is not available. + * BSS zero-initialization cleanup. + * Call misc_register() from rng_init_one. + * Fix O_NONBLOCK to occur before we schedule. + + Version 0.9.2: + * Simplify open blocking logic + + Version 0.9.1: + * Support i815 chipsets too (Matt Sottek) + * Fix reference counting when statically compiled (prumpf) + * Rewrite rng_dev_read (prumpf) + * Make module races less likely (prumpf) + * Small miscellaneous bug fixes (prumpf) + * Use pci table for PCI id list + + Version 0.9.0: + * Don't register a pci_driver, because we are really + using PCI bridge vendor/device ids, and someone + may want to register a driver for the bridge. (bug fix) + * Don't let the usage count go negative (bug fix) + * Clean up spinlocks (bug fix) + * Enable PCI device, if necessary (bug fix) + * iounmap on module unload (bug fix) + * If RNG chrdev is already in use when open(2) is called, + sleep until it is available. + * Remove redundant globals rng_allocated, rng_use_count + * Convert numeric globals to unsigned + * Module unload cleanup + + Version 0.6.2: + * Clean up spinlocks. Since we don't have any interrupts + to worry about, but we do have a timer to worry about, + we use spin_lock_bh everywhere except the timer function + itself. + * Fix module load/unload. + * Fix timer function and h/w enable/disable logic + * New timer interval sysctl + * Clean up sysctl names diff --git a/Documentation/i810_rng.txt b/Documentation/i810_rng.txt deleted file mode 100644 index 21992f820dd4..000000000000 --- a/Documentation/i810_rng.txt +++ /dev/null @@ -1,134 +0,0 @@ - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - -Introduction: - - The i810_rng device driver is software that makes use of a - special hardware feature on the Intel i8xx-based chipsets, - a Random Number Generator (RNG). - - In order to make effective use of this device driver, you - should download the support software as well. Download the - latest version of the "intel-rng-tools" package from the - i810_rng driver's official Web site: - - http://sourceforge.net/projects/gkernel/ - -About the Intel RNG hardware, from the firmware hub datasheet: - - The Firmware Hub integrates a Random Number Generator (RNG) - using thermal noise generated from inherently random quantum - mechanical properties of silicon. When not generating new random - bits the RNG circuitry will enter a low power state. Intel will - provide a binary software driver to give third party software - access to our RNG for use as a security feature. At this time, - the RNG is only to be used with a system in an OS-present state. - -Theory of operation: - - Character driver. Using the standard open() - and read() system calls, you can read random data from - the i810 RNG device. This data is NOT CHECKED by any - fitness tests, and could potentially be bogus (if the - hardware is faulty or has been tampered with). Data is only - output if the hardware "has-data" flag is set, but nevertheless - a security-conscious person would run fitness tests on the - data before assuming it is truly random. - - /dev/intel_rng is char device major 10, minor 183. - -Driver notes: - - * FIXME: support poll(2) - - NOTE: request_mem_region was removed, for two reasons: - 1) Only one RNG is supported by this driver, 2) The location - used by the RNG is a fixed location in MMIO-addressable memory, - 3) users with properly working BIOS e820 handling will always - have the region in which the RNG is located reserved, so - request_mem_region calls always fail for proper setups. - However, for people who use mem=XX, BIOS e820 information is - -not- in /proc/iomem, and request_mem_region(RNG_ADDR) can - succeed. - -Driver details: - - Based on: - Intel 82802AB/82802AC Firmware Hub (FWH) Datasheet - May 1999 Order Number: 290658-002 R - - Intel 82802 Firmware Hub: Random Number Generator - Programmer's Reference Manual - December 1999 Order Number: 298029-001 R - - Intel 82802 Firmware HUB Random Number Generator Driver - Copyright (c) 2000 Matt Sottek - - Special thanks to Matt Sottek. I did the "guts", he - did the "brains" and all the testing. - -Change history: - - Version 0.9.8: - * Support other i8xx chipsets by adding 82801E detection - * 82801DB detection is the same as for 82801CA. - - Version 0.9.7: - * Support other i8xx chipsets too (by adding 82801BA(M) and - 82801CA(M) detection) - - Version 0.9.6: - * Internal driver cleanups, prep for 1.0.0 release. - - Version 0.9.5: - * Rip out entropy injection via timer. It never ever worked, - and a better solution (rngd) is now available. - - Version 0.9.4: - * Fix: Remove request_mem_region - * Fix: Horrible bugs in FIPS calculation and test execution - - Version 0.9.3: - * Clean up rng_read a bit. - * Update i810_rng driver Web site URL. - * Increase default timer interval to 4 samples per second. - * Abort if mem region is not available. - * BSS zero-initialization cleanup. - * Call misc_register() from rng_init_one. - * Fix O_NONBLOCK to occur before we schedule. - - Version 0.9.2: - * Simplify open blocking logic - - Version 0.9.1: - * Support i815 chipsets too (Matt Sottek) - * Fix reference counting when statically compiled (prumpf) - * Rewrite rng_dev_read (prumpf) - * Make module races less likely (prumpf) - * Small miscellaneous bug fixes (prumpf) - * Use pci table for PCI id list - - Version 0.9.0: - * Don't register a pci_driver, because we are really - using PCI bridge vendor/device ids, and someone - may want to register a driver for the bridge. (bug fix) - * Don't let the usage count go negative (bug fix) - * Clean up spinlocks (bug fix) - * Enable PCI device, if necessary (bug fix) - * iounmap on module unload (bug fix) - * If RNG chrdev is already in use when open(2) is called, - sleep until it is available. - * Remove redundant globals rng_allocated, rng_use_count - * Convert numeric globals to unsigned - * Module unload cleanup - - Version 0.6.2: - * Clean up spinlocks. Since we don't have any interrupts - to worry about, but we do have a timer to worry about, - we use spin_lock_bh everywhere except the timer function - itself. - * Fix module load/unload. - * Fix timer function and h/w enable/disable logic - * New timer interval sysctl - * Clean up sysctl names diff --git a/drivers/char/amd768_rng.c b/drivers/char/amd768_rng.c deleted file mode 100644 index 6b059eef4556..000000000000 --- a/drivers/char/amd768_rng.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc - - derived from - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * core module and version information - */ -#define RNG_VERSION "0.1.0" -#define RNG_MODULE_NAME "amd768_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ - -static u32 pmbase; /* PMxx I/O base */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ - -static inline int rng_data_present (void) -{ - return inl(pmbase+0xF4) & 1; -} - - -static inline int rng_data_read (void) -{ - return inl(pmbase+0xF0); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - up(&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u32 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock(&rng_lock); - - have_data = 0; - if (rng_data_present()) { - data = rng_data_read(); - have_data = 4; - } - - spin_unlock (&rng_lock); - - while (have_data > 0) { - if (put_user((u8)data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - have_data--; - data>>=8; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if(need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); /* FIXME: We could poll for 250uS ?? */ - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 rnen; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - pci_read_config_dword(dev, 0x58, &pmbase); - - pmbase&=0x0000FF00; - - if(pmbase == 0) - { - printk (KERN_ERR PFX "power management base not set\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out_free_miscdev; - } - - pci_read_config_byte(dev, 0x40, &rnen); - rnen|=(1<<7); /* RNG on */ - pci_write_config_byte(dev, 0x40, rnen); - - pci_read_config_byte(dev, 0x41, &rnen); - rnen|=(1<<7); /* PMIO enable */ - pci_write_config_byte(dev, 0x41, rnen); - - printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - misc_deregister (&rng_miscdev); - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c new file mode 100644 index 000000000000..6b059eef4556 --- /dev/null +++ b/drivers/char/hw_random.c @@ -0,0 +1,295 @@ +/* + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc + + derived from + + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik + Copyright 2000,2001 Philipp Rumpf + + Please read Documentation/i810_rng.txt for details on use. + + ---------------------------------------------------------- + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * core module and version information + */ +#define RNG_VERSION "0.1.0" +#define RNG_MODULE_NAME "amd768_rng" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + + +/* + * debugging macros + */ +#undef RNG_DEBUG /* define to enable copious debugging info */ + +#ifdef RNG_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(!(expr)) { \ + printk( "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +#define RNG_MISCDEV_MINOR 183 /* official */ + +/* + * various RNG status variables. they are globals + * as we only support a single RNG device + */ + +static u32 pmbase; /* PMxx I/O base */ +static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ + + +/* + * inlined helper functions for accessing RNG registers + */ + +static inline int rng_data_present (void) +{ + return inl(pmbase+0xF4) & 1; +} + + +static inline int rng_data_read (void) +{ + return inl(pmbase+0xF0); +} + +static int rng_dev_open (struct inode *inode, struct file *filp) +{ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + + /* wait for device to become free */ + if (filp->f_flags & O_NONBLOCK) { + if (down_trylock (&rng_open_sem)) + return -EAGAIN; + } else { + if (down_interruptible (&rng_open_sem)) + return -ERESTARTSYS; + } + return 0; +} + + +static int rng_dev_release (struct inode *inode, struct file *filp) +{ + up(&rng_open_sem); + return 0; +} + + +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp) +{ + static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; + int have_data; + u32 data = 0; + ssize_t ret = 0; + + while (size) { + spin_lock(&rng_lock); + + have_data = 0; + if (rng_data_present()) { + data = rng_data_read(); + have_data = 4; + } + + spin_unlock (&rng_lock); + + while (have_data > 0) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(need_resched()) + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + else + udelay(200); /* FIXME: We could poll for 250uS ?? */ + + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + + +static struct file_operations rng_chrdev_ops = { + .owner = THIS_MODULE, + .open = rng_dev_open, + .release = rng_dev_release, + .read = rng_dev_read, +}; + + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + + +/* + * rng_init_one - look for and attempt to init a single RNG + */ +static int __init rng_init_one (struct pci_dev *dev) +{ + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + rc = misc_register (&rng_miscdev); + if (rc) { + printk (KERN_ERR PFX "cannot register misc device\n"); + DPRINTK ("EXIT, returning %d\n", rc); + goto err_out; + } + + pci_read_config_dword(dev, 0x58, &pmbase); + + pmbase&=0x0000FF00; + + if(pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + DPRINTK ("EXIT, returning %d\n", rc); + goto err_out_free_miscdev; + } + + pci_read_config_byte(dev, 0x40, &rnen); + rnen|=(1<<7); /* RNG on */ + pci_write_config_byte(dev, 0x40, rnen); + + pci_read_config_byte(dev, 0x41, &rnen); + rnen|=(1<<7); /* PMIO enable */ + pci_write_config_byte(dev, 0x41, rnen); + + printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_miscdev: + misc_deregister (&rng_miscdev); +err_out: + return rc; +} + + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id rng_pci_tbl[] __initdata = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, + { 0, }, +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); + + +MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); +MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); + + +/* + * rng_init - initialize RNG module + */ +static int __init rng_init (void) +{ + int rc; + struct pci_dev *pdev; + + DPRINTK ("ENTER\n"); + + init_MUTEX (&rng_open_sem); + + pci_for_each_dev(pdev) { + if (pci_match_device (rng_pci_tbl, pdev) != NULL) + goto match; + } + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + rc = rng_init_one (pdev); + if (rc) + return rc; + + printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + + +/* + * rng_init - shutdown RNG module + */ +static void __exit rng_cleanup (void) +{ + DPRINTK ("ENTER\n"); + misc_deregister (&rng_miscdev); + DPRINTK ("EXIT\n"); +} + + +module_init (rng_init); +module_exit (rng_cleanup); diff --git a/drivers/char/i810_rng.c b/drivers/char/i810_rng.c deleted file mode 100644 index 52671e065ede..000000000000 --- a/drivers/char/i810_rng.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik - Copyright 2000,2001 Philipp Rumpf - - Driver Web site: http://sourceforge.net/projects/gkernel/ - - Please read Documentation/i810_rng.txt for details on use. - - ---------------------------------------------------------- - - This software may be used and distributed according to the terms - of the GNU General Public License, incorporated herein by reference. - - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * core module and version information - */ -#define RNG_VERSION "0.9.8" -#define RNG_MODULE_NAME "i810_rng" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ -#undef RNG_DEBUG /* define to enable copious debugging info */ - -#ifdef RNG_DEBUG -/* note: prints function name for you */ -#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) -#else -#define DPRINTK(fmt, args...) -#endif - -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(!(expr)) { \ - printk( "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ - } -#endif - - -/* - * RNG registers (offsets from rng_mem) - */ -#define RNG_HW_STATUS 0 -#define RNG_PRESENT 0x40 -#define RNG_ENABLED 0x01 -#define RNG_STATUS 1 -#define RNG_DATA_PRESENT 0x01 -#define RNG_DATA 2 - -/* - * Magic address at which Intel PCI bridges locate the RNG - */ -#define RNG_ADDR 0xFFBC015F -#define RNG_ADDR_LEN 3 - -#define RNG_MISCDEV_MINOR 183 /* official */ - -/* - * various RNG status variables. they are globals - * as we only support a single RNG device - */ -static void *rng_mem; /* token to our ioremap'd RNG register area */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - - -/* - * inlined helper functions for accessing RNG registers - */ -static inline u8 rng_hwstatus (void) -{ - assert (rng_mem != NULL); - return readb (rng_mem + RNG_HW_STATUS); -} - -static inline u8 rng_hwstatus_set (u8 hw_status) -{ - assert (rng_mem != NULL); - writeb (hw_status, rng_mem + RNG_HW_STATUS); - return rng_hwstatus (); -} - - -static inline int rng_data_present (void) -{ - assert (rng_mem != NULL); - - return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0; -} - - -static inline int rng_data_read (void) -{ - assert (rng_mem != NULL); - - return readb (rng_mem + RNG_DATA); -} - -/* - * rng_enable - enable the RNG hardware - */ - -static int rng_enable (void) -{ - int rc = 0; - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if ((hw_status & RNG_ENABLED) == 0) { - new_status = rng_hwstatus_set (hw_status | RNG_ENABLED); - - if (new_status & RNG_ENABLED) - printk (KERN_INFO PFX "RNG h/w enabled\n"); - else { - printk (KERN_ERR PFX "Unable to enable the RNG\n"); - rc = -EIO; - } - } - - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - -/* - * rng_disable - disable the RNG hardware - */ - -static void rng_disable(void) -{ - u8 hw_status, new_status; - - DPRINTK ("ENTER\n"); - - hw_status = rng_hwstatus (); - - if (hw_status & RNG_ENABLED) { - new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - - if ((new_status & RNG_ENABLED) == 0) - printk (KERN_INFO PFX "RNG h/w disabled\n"); - else { - printk (KERN_ERR PFX "Unable to disable the RNG\n"); - } - } - - DPRINTK ("EXIT\n"); -} - -static int rng_dev_open (struct inode *inode, struct file *filp) -{ - int rc; - - if ((filp->f_mode & FMODE_READ) == 0) - return -EINVAL; - if (filp->f_mode & FMODE_WRITE) - return -EINVAL; - - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - - rc = rng_enable (); - if (rc) { - up (&rng_open_sem); - return rc; - } - - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - rng_disable (); - up (&rng_open_sem); - return 0; -} - - -static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, - loff_t * offp) -{ - static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; - u8 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock (&rng_lock); - - have_data = 0; - if (rng_data_present ()) { - data = rng_data_read (); - have_data = 1; - } - - spin_unlock (&rng_lock); - - if (have_data) { - if (put_user (data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if (need_resched()) - { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); - } - else - udelay(200); - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - - return ret; -} - - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - u8 hw_status; - - DPRINTK ("ENTER\n"); - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN); - if (rng_mem == NULL) { - printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); - DPRINTK ("EXIT, returning -EBUSY\n"); - rc = -EBUSY; - goto err_out_free_miscdev; - } - - /* Check for Intel 82802 */ - hw_status = rng_hwstatus (); - if ((hw_status & RNG_PRESENT) == 0) { - printk (KERN_ERR PFX "RNG not detected\n"); - DPRINTK ("EXIT, returning -ENODEV\n"); - rc = -ENODEV; - goto err_out_free_map; - } - - /* turn RNG h/w off, if it's on */ - if (hw_status & RNG_ENABLED) - hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED); - if (hw_status & RNG_ENABLED) { - printk (KERN_ERR PFX "cannot disable RNG, aborting\n"); - goto err_out_free_map; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_map: - iounmap (rng_mem); -err_out_free_miscdev: - misc_deregister (&rng_miscdev); -err_out: - return rc; -} - - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev; - - DPRINTK ("ENTER\n"); - - init_MUTEX (&rng_open_sem); - - pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) - goto match; - } - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - printk (KERN_INFO RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - - misc_deregister (&rng_miscdev); - - iounmap (rng_mem); - - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); -- cgit v1.2.3 From aaf9c531ea73d41b80e597dc3b2a8c8657a9ab4f Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 13 Mar 2003 08:24:13 -0500 Subject: [hw_random] update amd768_rng driver to be modular; add Intel support Take Alan's amd768_rng driver, recently renamed to hw_random.c, and convert it's very-simple structure to support multiple types of hardware RNG. Integrate Intel i8xx (ICH) RNG support. --- drivers/char/Kconfig | 31 +--- drivers/char/Makefile | 3 +- drivers/char/hw_random.c | 363 +++++++++++++++++++++++++++++++++++++---------- 3 files changed, 294 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d98a11175015..df1c09b28700 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -709,39 +709,20 @@ config NWFLASH If you're not sure, say N. -config INTEL_RNG - tristate "Intel i8x0 Random Number Generator support" +config HW_RANDOM + tristate "Intel/AMD H/W Random Number Generator support" depends on (X86 || IA64) && PCI ---help--- This driver provides kernel-side support for the Random Number - Generator hardware found on Intel i8xx-based motherboards. + Generator hardware found on Intel i8xx-based motherboards, + and AMD 76x-based motherboards. - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. + Provides a character driver, used to read() entropy data. To compile this driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The module will be called - i810_rng. - - If unsure, say N. - -config AMD_RNG - tristate "AMD 768 Random Number Generator support" - depends on X86 && PCI - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on AMD 76x based motherboards. - - Both a character driver, used to read() entropy data, and a timer - function which automatically adds entropy directly into the - kernel pool, are exported by this driver. - - To compile this driver as a module ( = code which can be inserted in - and removed from the running kernel whenever you want), say M here - and read . The module will be called - amd768_rng. + hw_random. If unsure, say N. diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 147b19429bf5..b03db0a153c0 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -59,8 +59,7 @@ endif obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o -obj-$(CONFIG_INTEL_RNG) += i810_rng.o -obj-$(CONFIG_AMD_RNG) += amd768_rng.o +obj-$(CONFIG_HW_RANDOM) += hw_random.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_H8) += h8.o diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c index 6b059eef4556..71224f37ddaa 100644 --- a/drivers/char/hw_random.c +++ b/drivers/char/hw_random.c @@ -1,14 +1,19 @@ /* - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc + Hardware driver for the Intel/AMD Random Number Generators (RNG) + (c) Copyright 2003 Red Hat Inc derived from + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc + + derived from + Hardware driver for Intel i810 Random Number Generator (RNG) Copyright 2000,2001 Jeff Garzik Copyright 2000,2001 Philipp Rumpf - Please read Documentation/i810_rng.txt for details on use. + Please read Documentation/hw_random.txt for details on use. ---------------------------------------------------------- This software may be used and distributed according to the terms @@ -37,8 +42,8 @@ /* * core module and version information */ -#define RNG_VERSION "0.1.0" -#define RNG_MODULE_NAME "amd768_rng" +#define RNG_VERSION "0.9.0" +#define RNG_MODULE_NAME "hw_random" #define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION #define PFX RNG_MODULE_NAME ": " @@ -55,7 +60,7 @@ #define DPRINTK(fmt, args...) #endif -#undef RNG_NDEBUG /* define to disable lightweight runtime checks */ +#define RNG_NDEBUG /* define to disable lightweight runtime checks */ #ifdef RNG_NDEBUG #define assert(expr) #else @@ -68,30 +73,269 @@ #define RNG_MISCDEV_MINOR 183 /* official */ +static int rng_dev_open (struct inode *inode, struct file *filp); +static int rng_dev_release (struct inode *inode, struct file *filp); +static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, + loff_t * offp); + +static int __init intel_init (struct pci_dev *dev); +static void __exit intel_cleanup(void); +static unsigned int intel_data_present (void); +static u32 intel_data_read (void); + +static int __init amd_init (struct pci_dev *dev); +static void __exit amd_cleanup(void); +static unsigned int amd_data_present (void); +static u32 amd_data_read (void); + +struct rng_operations { + int (*init) (struct pci_dev *dev); + void (*cleanup) (void); + unsigned int (*data_present) (void); + u32 (*data_read) (void); + unsigned int n_bytes; /* number of bytes per ->data_read */ +}; +static struct rng_operations *rng_ops; + +static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ + +static struct file_operations rng_chrdev_ops = { + .owner = THIS_MODULE, + .open = rng_dev_open, + .release = rng_dev_release, + .read = rng_dev_read, +}; + + +static struct miscdevice rng_miscdev = { + RNG_MISCDEV_MINOR, + RNG_MODULE_NAME, + &rng_chrdev_ops, +}; + +enum { + rng_hw_none, + rng_hw_intel, + rng_hw_amd, +}; + +static struct rng_operations rng_vendor_ops[] __initdata = { + /* rng_hw_none */ + { }, + + /* rng_hw_intel */ + { intel_init, intel_cleanup, intel_data_present, + intel_data_read, 1 }, + + /* rng_hw_amd */ + { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 }, +}; + /* - * various RNG status variables. they are globals - * as we only support a single RNG device + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. */ +static struct pci_device_id rng_pci_tbl[] __initdata = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, -static u32 pmbase; /* PMxx I/O base */ -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); +/*********************************************************************** + * + * Intel RNG operations + * + */ + /* - * inlined helper functions for accessing RNG registers + * RNG registers (offsets from rng_mem) */ +#define INTEL_RNG_HW_STATUS 0 +#define INTEL_RNG_PRESENT 0x40 +#define INTEL_RNG_ENABLED 0x01 +#define INTEL_RNG_STATUS 1 +#define INTEL_RNG_DATA_PRESENT 0x01 +#define INTEL_RNG_DATA 2 -static inline int rng_data_present (void) +/* + * Magic address at which Intel PCI bridges locate the RNG + */ +#define INTEL_RNG_ADDR 0xFFBC015F +#define INTEL_RNG_ADDR_LEN 3 + +/* token to our ioremap'd RNG register area */ +static void *rng_mem; + +static inline u8 intel_hwstatus (void) +{ + assert (rng_mem != NULL); + return readb (rng_mem + INTEL_RNG_HW_STATUS); +} + +static inline u8 intel_hwstatus_set (u8 hw_status) { - return inl(pmbase+0xF4) & 1; + assert (rng_mem != NULL); + writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS); + return intel_hwstatus (); } +static unsigned int intel_data_present(void) +{ + assert (rng_mem != NULL); + + return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ? + 1 : 0; +} -static inline int rng_data_read (void) +static u32 intel_data_read(void) { - return inl(pmbase+0xF0); + assert (rng_mem != NULL); + + return readb (rng_mem + INTEL_RNG_DATA); } +static int __init intel_init (struct pci_dev *dev) +{ + int rc; + u8 hw_status; + + DPRINTK ("ENTER\n"); + + rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); + if (rng_mem == NULL) { + printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); + rc = -EBUSY; + goto err_out; + } + + /* Check for Intel 82802 */ + hw_status = intel_hwstatus (); + if ((hw_status & INTEL_RNG_PRESENT) == 0) { + printk (KERN_ERR PFX "RNG not detected\n"); + rc = -ENODEV; + goto err_out_free_map; + } + + /* turn RNG h/w on, if it's off */ + if ((hw_status & INTEL_RNG_ENABLED) == 0) + hw_status = intel_hwstatus_set (hw_status | INTEL_RNG_ENABLED); + if ((hw_status & INTEL_RNG_ENABLED) == 0) { + printk (KERN_ERR PFX "cannot enable RNG, aborting\n"); + rc = -EIO; + goto err_out_free_map; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_map: + iounmap (rng_mem); + rng_mem = NULL; +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void __exit intel_cleanup(void) +{ + u8 hw_status; + + hw_status = intel_hwstatus (); + if (hw_status & INTEL_RNG_ENABLED) + intel_hwstatus_set (hw_status & ~INTEL_RNG_ENABLED); + else + printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); + iounmap(rng_mem); + rng_mem = NULL; +} + +/*********************************************************************** + * + * AMD RNG operations + * + */ + +static u32 pmbase; /* PMxx I/O base */ +static struct pci_dev *amd_dev; + +static unsigned int amd_data_present (void) +{ + return inl(pmbase + 0xF4) & 1; +} + + +static u32 amd_data_read (void) +{ + return inl(pmbase + 0xF0); +} + +static int __init amd_init (struct pci_dev *dev) +{ + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + pci_read_config_dword(dev, 0x58, &pmbase); + + pmbase &= 0x0000FF00; + + if (pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + rc = -EIO; + goto err_out; + } + + pci_read_config_byte(dev, 0x40, &rnen); + rnen |= (1 << 7); /* RNG on */ + pci_write_config_byte(dev, 0x40, rnen); + + pci_read_config_byte(dev, 0x41, &rnen); + rnen |= (1 << 7); /* PMIO enable */ + pci_write_config_byte(dev, 0x41, rnen); + + printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); + + amd_dev = dev; + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void __exit amd_cleanup(void) +{ + u8 rnen; + + pci_read_config_byte(amd_dev, 0x40, &rnen); + rnen &= ~(1 << 7); /* RNG off */ + pci_write_config_byte(amd_dev, 0x40, rnen); + + /* FIXME: twiddle pmio, also? */ +} + +/*********************************************************************** + * + * /dev/hwrandom character device handling (major 10, minor 183) + * + */ + static int rng_dev_open (struct inode *inode, struct file *filp) { if ((filp->f_mode & FMODE_READ) == 0) @@ -122,7 +366,7 @@ static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, loff_t * offp) { static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED; - int have_data; + unsigned int have_data; u32 data = 0; ssize_t ret = 0; @@ -130,9 +374,9 @@ static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, spin_lock(&rng_lock); have_data = 0; - if (rng_data_present()) { - data = rng_data_read(); - have_data = 4; + if (rng_ops->data_present()) { + data = rng_ops->data_read(); + have_data = rng_ops->n_bytes; } spin_unlock (&rng_lock); @@ -166,20 +410,6 @@ static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, } -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .release = rng_dev_release, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - /* * rng_init_one - look for and attempt to init a single RNG @@ -187,64 +417,35 @@ static struct miscdevice rng_miscdev = { static int __init rng_init_one (struct pci_dev *dev) { int rc; - u8 rnen; DPRINTK ("ENTER\n"); - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "cannot register misc device\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out; - } - - pci_read_config_dword(dev, 0x58, &pmbase); + assert(rng_ops != NULL); - pmbase&=0x0000FF00; + rc = rng_ops->init(dev); + if (rc) + goto err_out; - if(pmbase == 0) - { - printk (KERN_ERR PFX "power management base not set\n"); - DPRINTK ("EXIT, returning %d\n", rc); - goto err_out_free_miscdev; + rc = misc_register (&rng_miscdev); + if (rc) { + printk (KERN_ERR PFX "misc device register failed\n"); + goto err_out_cleanup_hw; } - pci_read_config_byte(dev, 0x40, &rnen); - rnen|=(1<<7); /* RNG on */ - pci_write_config_byte(dev, 0x40, rnen); - - pci_read_config_byte(dev, 0x41, &rnen); - rnen|=(1<<7); /* PMIO enable */ - pci_write_config_byte(dev, 0x41, rnen); - - printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase); DPRINTK ("EXIT, returning 0\n"); return 0; -err_out_free_miscdev: - misc_deregister (&rng_miscdev); +err_out_cleanup_hw: + rng_ops->cleanup(); err_out: + DPRINTK ("EXIT, returning %d\n", rc); return rc; } -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] __initdata = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, }, -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - -MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek"); -MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver"); +MODULE_AUTHOR("The Linux Kernel team"); +MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); MODULE_LICENSE("GPL"); @@ -255,14 +456,19 @@ static int __init rng_init (void) { int rc; struct pci_dev *pdev; + const struct pci_device_id *ent; DPRINTK ("ENTER\n"); init_MUTEX (&rng_open_sem); + /* Probe for Intel, AMD RNGs */ pci_for_each_dev(pdev) { - if (pci_match_device (rng_pci_tbl, pdev) != NULL) + ent = pci_match_device (rng_pci_tbl, pdev); + if (ent) { + rng_ops = &rng_vendor_ops[ent->driver_data]; goto match; + } } DPRINTK ("EXIT, returning -ENODEV\n"); @@ -286,7 +492,12 @@ match: static void __exit rng_cleanup (void) { DPRINTK ("ENTER\n"); + misc_deregister (&rng_miscdev); + + if (rng_ops->cleanup) + rng_ops->cleanup(); + DPRINTK ("EXIT\n"); } -- cgit v1.2.3 From 7a96704c84272b13a39b42ec40ec1c483dcf1407 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 13 Mar 2003 08:48:48 -0500 Subject: [hw_random] add support for VIA Nehemiah RNG ("xstore" instruction) --- drivers/char/Kconfig | 2 +- drivers/char/hw_random.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index df1c09b28700..3220c9afc9ff 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -710,7 +710,7 @@ config NWFLASH If you're not sure, say N. config HW_RANDOM - tristate "Intel/AMD H/W Random Number Generator support" + tristate "Intel/AMD/Via H/W Random Number Generator support" depends on (X86 || IA64) && PCI ---help--- This driver provides kernel-side support for the Random Number diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c index 71224f37ddaa..9dddeafe9e86 100644 --- a/drivers/char/hw_random.c +++ b/drivers/char/hw_random.c @@ -1,5 +1,5 @@ /* - Hardware driver for the Intel/AMD Random Number Generators (RNG) + Hardware driver for the Intel/AMD/Via Random Number Generators (RNG) (c) Copyright 2003 Red Hat Inc derived from @@ -35,6 +35,11 @@ #include #include +#ifdef __i386__ +#include +#include +#endif + #include #include @@ -88,6 +93,11 @@ static void __exit amd_cleanup(void); static unsigned int amd_data_present (void); static u32 amd_data_read (void); +static int __init via_init(struct pci_dev *dev); +static void __exit via_cleanup(void); +static unsigned int via_data_present (void); +static u32 via_data_read (void); + struct rng_operations { int (*init) (struct pci_dev *dev); void (*cleanup) (void); @@ -117,6 +127,7 @@ enum { rng_hw_none, rng_hw_intel, rng_hw_amd, + rng_hw_via, }; static struct rng_operations rng_vendor_ops[] __initdata = { @@ -129,6 +140,9 @@ static struct rng_operations rng_vendor_ops[] __initdata = { /* rng_hw_amd */ { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 }, + + /* rng_hw_via */ + { via_init, via_cleanup, via_data_present, via_data_read, 1 }, }; /* @@ -330,6 +344,127 @@ static void __exit amd_cleanup(void) /* FIXME: twiddle pmio, also? */ } +/*********************************************************************** + * + * Via RNG operations + * + */ + +enum { + VIA_STRFILT_CNT_SHIFT = 16, + VIA_STRFILT_FAIL = (1 << 15), + VIA_STRFILT_ENABLE = (1 << 14), + VIA_RAWBITS_ENABLE = (1 << 13), + VIA_RNG_ENABLE = (1 << 6), + VIA_XSTORE_CNT_MASK = 0x0F, + + VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ + VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, + VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_2_MASK = 0xFFFF, + VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_1_MASK = 0xFF, +}; + +u32 via_rng_datum; + +/* + * Investigate using the 'rep' prefix to obtain 32 bits of random data + * in one insn. The upside is potentially better performance. The + * downside is that the instruction becomes no longer atomic. Due to + * this, just like familiar issues with /dev/random itself, the worst + * case of a 'rep xstore' could potentially pause a cpu for an + * unreasonably long time. In practice, this condition would likely + * only occur when the hardware is failing. (or so we hope :)) + * + * Another possible performance boost may come from simply buffering + * until we have 4 bytes, thus returning a u32 at a time, + * instead of the current u8-at-a-time. + */ + +static inline u32 xstore(u32 *addr, u32 edx_in) +{ + u32 eax_out; + + asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" + :"=m"(*addr), "=a"(eax_out) + :"D"(addr), "d"(edx_in)); + + return eax_out; +} + +static unsigned int via_data_present(void) +{ + u32 bytes_out; + + /* We choose the recommended 1-byte-per-instruction RNG rate, + * for greater randomness at the expense of speed. Larger + * values 2, 4, or 8 bytes-per-instruction yield greater + * speed at lesser randomness. + * + * If you change this to another VIA_CHUNK_n, you must also + * change the ->n_bytes values in rng_vendor_ops[] tables. + * VIA_CHUNK_8 requires further code changes. + * + * A copy of MSR_VIA_RNG is placed in eax_out when xstore + * completes. + */ + via_rng_datum = 0; /* paranoia, not really necessary */ + bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK; + if (bytes_out == 0) + return 0; + + return 1; +} + +static u32 via_data_read(void) +{ + return via_rng_datum; +} + +static int __init via_init(struct pci_dev *dev) +{ + u32 lo, hi, old_lo; + + /* Control the RNG via MSR. Tread lightly and pay very close + * close attention to values written, as the reserved fields + * are documented to be "undefined and unpredictable"; but it + * does not say to write them as zero, so I make a guess that + * we restore the values we find in the register. + */ + rdmsr(MSR_VIA_RNG, lo, hi); + + old_lo = lo; + lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); + lo &= ~VIA_XSTORE_CNT_MASK; + lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); + lo |= VIA_RNG_ENABLE; + + if (lo != old_lo) + wrmsr(MSR_VIA_RNG, lo, hi); + + /* perhaps-unnecessary sanity check; remove after testing if + unneeded */ + rdmsr(MSR_VIA_RNG, lo, hi); + if ((lo & VIA_RNG_ENABLE) == 0) { + printk(KERN_ERR PFX "cannot enable Via C3 RNG, aborting\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit via_cleanup(void) +{ + u32 lo, hi; + + rdmsr(MSR_VIA_RNG, lo, hi); + lo &= ~VIA_RNG_ENABLE; + wrmsr(MSR_VIA_RNG, lo, hi); +} + + /*********************************************************************** * * /dev/hwrandom character device handling (major 10, minor 183) @@ -471,6 +606,15 @@ static int __init rng_init (void) } } +#ifdef __i386__ + /* Probe for Via RNG */ + if (cpu_has_xstore) { + rng_ops = &rng_vendor_ops[rng_hw_via]; + pdev = NULL; + goto match; + } +#endif + DPRINTK ("EXIT, returning -ENODEV\n"); return -ENODEV; -- cgit v1.2.3 From 1c6604f184d1d1ba158ddef850c8b834f3103d43 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 13 Mar 2003 08:51:25 -0500 Subject: [hw_random] fixes and cleanups * s/Via/VIA/ * allow multiple simultaneous open(2)s of the chrdev. This allows us to eliminate some code, without modifying the core code (rng_dev_read) at all. * s/__exit// in ->cleanup ops, to eliminate link error --- drivers/char/Kconfig | 4 ++-- drivers/char/hw_random.c | 42 +++++++++++------------------------------- 2 files changed, 13 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3220c9afc9ff..ebc50ca263d2 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -710,12 +710,12 @@ config NWFLASH If you're not sure, say N. config HW_RANDOM - tristate "Intel/AMD/Via H/W Random Number Generator support" + tristate "Intel/AMD/VIA HW Random Number Generator support" depends on (X86 || IA64) && PCI ---help--- This driver provides kernel-side support for the Random Number Generator hardware found on Intel i8xx-based motherboards, - and AMD 76x-based motherboards. + AMD 76x-based motherboards, and Via Nehemiah CPUs. Provides a character driver, used to read() entropy data. diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c index 9dddeafe9e86..a3a483e16aeb 100644 --- a/drivers/char/hw_random.c +++ b/drivers/char/hw_random.c @@ -1,5 +1,5 @@ /* - Hardware driver for the Intel/AMD/Via Random Number Generators (RNG) + Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) (c) Copyright 2003 Red Hat Inc derived from @@ -79,22 +79,21 @@ #define RNG_MISCDEV_MINOR 183 /* official */ static int rng_dev_open (struct inode *inode, struct file *filp); -static int rng_dev_release (struct inode *inode, struct file *filp); static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size, loff_t * offp); static int __init intel_init (struct pci_dev *dev); -static void __exit intel_cleanup(void); +static void intel_cleanup(void); static unsigned int intel_data_present (void); static u32 intel_data_read (void); static int __init amd_init (struct pci_dev *dev); -static void __exit amd_cleanup(void); +static void amd_cleanup(void); static unsigned int amd_data_present (void); static u32 amd_data_read (void); static int __init via_init(struct pci_dev *dev); -static void __exit via_cleanup(void); +static void via_cleanup(void); static unsigned int via_data_present (void); static u32 via_data_read (void); @@ -107,12 +106,9 @@ struct rng_operations { }; static struct rng_operations *rng_ops; -static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */ - static struct file_operations rng_chrdev_ops = { .owner = THIS_MODULE, .open = rng_dev_open, - .release = rng_dev_release, .read = rng_dev_read, }; @@ -262,7 +258,7 @@ err_out: return rc; } -static void __exit intel_cleanup(void) +static void intel_cleanup(void) { u8 hw_status; @@ -333,7 +329,7 @@ err_out: return rc; } -static void __exit amd_cleanup(void) +static void amd_cleanup(void) { u8 rnen; @@ -346,7 +342,7 @@ static void __exit amd_cleanup(void) /*********************************************************************** * - * Via RNG operations + * VIA RNG operations * */ @@ -448,14 +444,14 @@ static int __init via_init(struct pci_dev *dev) unneeded */ rdmsr(MSR_VIA_RNG, lo, hi); if ((lo & VIA_RNG_ENABLE) == 0) { - printk(KERN_ERR PFX "cannot enable Via C3 RNG, aborting\n"); + printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); return -ENODEV; } return 0; } -static void __exit via_cleanup(void) +static void via_cleanup(void) { u32 lo, hi; @@ -473,26 +469,12 @@ static void __exit via_cleanup(void) static int rng_dev_open (struct inode *inode, struct file *filp) { + /* enforce read-only access to this chrdev */ if ((filp->f_mode & FMODE_READ) == 0) return -EINVAL; if (filp->f_mode & FMODE_WRITE) return -EINVAL; - /* wait for device to become free */ - if (filp->f_flags & O_NONBLOCK) { - if (down_trylock (&rng_open_sem)) - return -EAGAIN; - } else { - if (down_interruptible (&rng_open_sem)) - return -ERESTARTSYS; - } - return 0; -} - - -static int rng_dev_release (struct inode *inode, struct file *filp) -{ - up(&rng_open_sem); return 0; } @@ -595,8 +577,6 @@ static int __init rng_init (void) DPRINTK ("ENTER\n"); - init_MUTEX (&rng_open_sem); - /* Probe for Intel, AMD RNGs */ pci_for_each_dev(pdev) { ent = pci_match_device (rng_pci_tbl, pdev); @@ -607,7 +587,7 @@ static int __init rng_init (void) } #ifdef __i386__ - /* Probe for Via RNG */ + /* Probe for VIA RNG */ if (cpu_has_xstore) { rng_ops = &rng_vendor_ops[rng_hw_via]; pdev = NULL; -- cgit v1.2.3 From bf056a3be2319d115f6ef2097930e2cf09ff4a49 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 13 Mar 2003 18:36:55 -0800 Subject: [PATCH] USB ohci: "registers" sysfs file > This exhibits a build error when OHCI_VERBOSE_DEBUG is enabled: Odd, I guess the build I tested was when that was enabled without first enabling debugging. The fix is trivial. --- drivers/usb/host/ohci-q.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index a25daa6c899f..f531767a9ecd 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -650,7 +650,7 @@ static void td_submit_urb ( /* calculate transfer length/status and update the urb * PRECONDITION: irqsafe (only for urb->status locking) */ -static void td_done (struct urb *urb, struct td *td) +static void td_done (struct ohci_hcd *ohci, struct urb *urb, struct td *td) { u32 tdINFO = le32_to_cpup (&td->hwINFO); int cc = 0; @@ -908,7 +908,7 @@ rescan_this: *prev = td->hwNextTD | savebits; /* HC may have partly processed this TD */ - td_done (urb, td); + td_done (ohci, urb, td); urb_priv->td_cnt++; /* if URB is done, clean up */ @@ -991,7 +991,7 @@ dl_done_list (struct ohci_hcd *ohci, struct td *td, struct pt_regs *regs) struct ed *ed = td->ed; /* update URB's length and status from TD */ - td_done (urb, td); + td_done (ohci, urb, td); urb_priv->td_cnt++; /* If all this urb's TDs are done, call complete() */ -- cgit v1.2.3 From b2b54c70b38aeff7fc09071ce52e482cfb193db3 Mon Sep 17 00:00:00 2001 From: Henning Meier-Geinitz Date: Thu, 13 Mar 2003 18:37:16 -0800 Subject: [PATCH] USB: Fix crash in read/write/ioctl in scanner driver Used kobject reference counting to free the scn struct when the device is closed and disconnected. Avoids crashes when writing to a disconnected device. (Thanks to Greg KH). I've also changed irq_scanner to avoid submitting new URBs when the old one returned with an error. Without this change irq_scanner gets called ever and ever again after a disconnect while open. --- drivers/usb/image/scanner.c | 70 +++++++++++++++++++++++++++++++-------------- drivers/usb/image/scanner.h | 4 ++- 2 files changed, 51 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c index c8d1dd5e0835..f1d758d78cf7 100644 --- a/drivers/usb/image/scanner.c +++ b/drivers/usb/image/scanner.c @@ -1,7 +1,7 @@ /* -*- linux-c -*- */ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Copyright (C) 2002, 2003 Henning Meier-Geinitz @@ -350,6 +350,9 @@ * - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax, * Prolink, Fujitsu, Plustek, and SYSCAN scanners. * - Fixed generation of devfs names if dynamic minors are disabled. + * - Used kobject reference counting to free the scn struct when the device + * is closed and disconnected. Avoids crashes when writing to a + * disconnected device. (Thanks to Greg KH). * * TODO * - Performance @@ -427,6 +430,7 @@ irq_scanner(struct urb *urb, struct pt_regs *regs) return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + return; } dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data); @@ -461,6 +465,7 @@ open_scanner(struct inode * inode, struct file * file) return -ENODEV; } scn = usb_get_intfdata(intf); + kobject_get(&scn->kobj); dev = scn->scn_dev; @@ -521,6 +526,8 @@ close_scanner(struct inode * inode, struct file * file) up(&scn_mutex); up(&(scn->sem)); + kobject_put(&scn->kobj); + return 0; } @@ -813,6 +820,37 @@ ioctl_scanner(struct inode *inode, struct file *file, return retval; } +static void destroy_scanner (struct kobject *kobj) +{ + struct scn_usb_data *scn; + + dbg ("%s", __FUNCTION__); + + scn = to_scanner(kobj); + + down (&scn_mutex); + down (&(scn->sem)); + + usb_driver_release_interface(&scanner_driver, + &scn->scn_dev->actconfig->interface[scn->ifnum]); + + kfree(scn->ibuf); + kfree(scn->obuf); + + dbg("%s: De-allocating minor:%d", __FUNCTION__, scn->scn_minor); + devfs_unregister(scn->devfs); + usb_deregister_dev(1, scn->scn_minor); + usb_free_urb(scn->scn_irq); + usb_put_dev(scn->scn_dev); + up (&(scn->sem)); + kfree (scn); + up (&scn_mutex); +} + +static struct kobj_type scanner_kobj_type = { + .release = destroy_scanner, +}; + static struct file_operations usb_scanner_fops = { .owner = THIS_MODULE, @@ -982,6 +1020,8 @@ probe_scanner(struct usb_interface *intf, return -ENOMEM; } memset (scn, 0, sizeof(struct scn_usb_data)); + kobject_init(&scn->kobj); + scn->kobj.ktype = &scanner_kobj_type; scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL); if (!scn->scn_irq) { @@ -1049,6 +1089,7 @@ probe_scanner(struct usb_interface *intf, } + usb_get_dev(dev); scn->bulk_in_ep = have_bulk_in; scn->bulk_out_ep = have_bulk_out; scn->intr_ep = have_intr; @@ -1089,28 +1130,13 @@ disconnect_scanner(struct usb_interface *intf) intf->kdev = NODEV; usb_set_intfdata(intf, NULL); - if (scn) { - down (&scn_mutex); - down (&(scn->sem)); - - if(scn->intr_ep) { - dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor); - usb_unlink_urb(scn->scn_irq); - } - usb_driver_release_interface(&scanner_driver, - &scn->scn_dev->actconfig->interface[scn->ifnum]); - - kfree(scn->ibuf); - kfree(scn->obuf); - - dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); - devfs_unregister(scn->devfs); - usb_deregister_dev(1, scn->scn_minor); - usb_free_urb(scn->scn_irq); - up (&(scn->sem)); - kfree (scn); - up (&scn_mutex); + if(scn->intr_ep) { + dbg("%s(%d): Unlinking IRQ URB", __FUNCTION__, scn->scn_minor); + usb_unlink_urb(scn->scn_irq); } + + if (scn) + kobject_put(&scn->kobj); } /* we want to look at all devices, as the vendor/product id can change diff --git a/drivers/usb/image/scanner.h b/drivers/usb/image/scanner.h index 93be043c7a62..dc77eb090dec 100644 --- a/drivers/usb/image/scanner.h +++ b/drivers/usb/image/scanner.h @@ -1,5 +1,5 @@ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Previously maintained by Brian Beattie @@ -335,7 +335,9 @@ struct scn_usb_data { wait_queue_head_t rd_wait_q; /* read timeouts */ struct semaphore sem; /* lock to prevent concurrent reads or writes */ unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */ + struct kobject kobj; /* Handles our reference counting */ }; +#define to_scanner(d) container_of(d, struct scn_usb_data, kobj) extern devfs_handle_t usb_devfs_handle; -- cgit v1.2.3 From 75499ce6b426ffe6b63546ccc44d55e5b4c84331 Mon Sep 17 00:00:00 2001 From: Johannes Erdfelt Date: Thu, 13 Mar 2003 18:43:09 -0800 Subject: [PATCH] uhci-hcd.c 2.5 finish completions in correct order Here's the 2.5 version of the patch to uhci.c to finish completions in the correct order. --- drivers/usb/host/uhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c3c0a7505765..06541479c29f 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -136,7 +136,7 @@ static inline void uhci_add_complete(struct uhci_hcd *uhci, struct urb *urb) unsigned long flags; spin_lock_irqsave(&uhci->complete_list_lock, flags); - list_add(&urbp->complete_list, &uhci->complete_list); + list_add_tail(&urbp->complete_list, &uhci->complete_list); spin_unlock_irqrestore(&uhci->complete_list_lock, flags); } -- cgit v1.2.3 From c15fc0016eed0a267f23f5f4b2d23a15a8746b62 Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Thu, 13 Mar 2003 18:43:24 -0800 Subject: [PATCH] Memleak in KOBIL USB Smart Card Terminal Driver There is a memleak on error exit path in KOBIL USB Smart Card Terminal Driver in both current 2.4 and 2.5. See the patch. Found with help of smatch + enhanced unfree script. --- drivers/usb/serial/kobil_sct.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 69abd70e79cf..99c634017192 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -253,6 +253,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) port->write_urb = usb_alloc_urb(0, GFP_KERNEL); if (!port->write_urb) { dbg("%s - port %d usb_alloc_urb failed", __FUNCTION__, port->number); + kfree(transfer_buffer); return -1; } } @@ -260,6 +261,7 @@ static int kobil_open (struct usb_serial_port *port, struct file *filp) // allocate memory for write_urb transfer buffer port->write_urb->transfer_buffer = (unsigned char *) kmalloc(write_urb_transfer_buffer_length, GFP_KERNEL); if (! port->write_urb->transfer_buffer) { + kfree(transfer_buffer); return -1; } -- cgit v1.2.3 From 67deed367d214114e93e820849d05ed0f09d8d21 Mon Sep 17 00:00:00 2001 From: Markus Demleitner Date: Thu, 13 Mar 2003 18:53:01 -0800 Subject: [PATCH] USB: Patch for DSBR-100 driver I since you are listed as the maintainer of the USB subsystem and I can't really see who else applies, I'm sending you a patch to my driver for the DSBR-100 USB radio. This is mainly code cosmetics (fixed ugly missing spaces after commas I inherited from the aztech driver, some constants moved to preprocessor symbols), but there's one technical change: I used to stop the radio when my file descriptor was closed. Petr Slansky pointed out that the other radio drivers don't do that, so now I just let the radio run. --- drivers/usb/media/dsbr100.c | 75 +++++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c index cc099cb8d91d..cabf38496d33 100644 --- a/drivers/usb/media/dsbr100.c +++ b/drivers/usb/media/dsbr100.c @@ -33,6 +33,12 @@ History: + Version 0.30: + Markus: Updates for 2.5.x kernel and more ISO compiant source + + Version 0.25: + PSL and Markus: Cleanup, radio now doesn't stop on device close + Version 0.24: Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally right. Some minor cleanup, improved standalone compilation @@ -69,15 +75,22 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.24" +#define DRIVER_VERSION "v0.25" #define DRIVER_AUTHOR "Markus Demleitner " -#define DRIVER_DESC "D-Link DSB-R100 USB radio driver" +#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 #define TB_LEN 16 +/* Frequency limits in MHz -- these are European values. For Japanese +devices, that would be 76 and 91. */ +#define FREQ_MIN 87.5 +#define FREQ_MAX 108.0 +#define FREQ_MUL 16000 + + static int usb_dsbr100_probe(struct usb_interface *intf, const struct usb_device_id *id); static void usb_dsbr100_disconnect(struct usb_interface *intf); @@ -108,7 +121,7 @@ static struct file_operations usb_dsbr100_fops = { static struct video_device usb_dsbr100_radio= { .owner = THIS_MODULE, - .name = "D-Link DSB R-100 USB radio", + .name = "D-Link DSB-R 100", .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_AZTECH, .fops = &usb_dsbr100_fops, @@ -189,7 +202,7 @@ static int usb_dsbr100_probe(struct usb_interface *intf, return -ENOMEM; usb_dsbr100_radio.priv = radio; radio->dev = interface_to_usbdev (intf); - radio->curfreq = 1454000; + radio->curfreq = FREQ_MIN*FREQ_MUL; usb_set_intfdata (intf, radio); return 0; } @@ -225,11 +238,11 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, { case VIDIOCGCAP: { struct video_capability *v = arg; - memset(v,0,sizeof(*v)); - v->type=VID_TYPE_TUNER; - v->channels=1; - v->audios=1; - strcpy(v->name, "D-Link R-100 USB Radio"); + memset(v, 0, sizeof(*v)); + v->type = VID_TYPE_TUNER; + v->channels = 1; + v->audios = 1; + strcpy(v->name, "D-Link R-100 USB FM Radio"); return 0; } case VIDIOCGTUNER: { @@ -237,8 +250,8 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, dsbr100_getstat(radio); if(v->tuner) /* Only 1 tuner */ return -EINVAL; - v->rangelow = 87*16000; - v->rangehigh = 108*16000; + v->rangelow = FREQ_MIN*FREQ_MUL; + v->rangehigh = FREQ_MAX*FREQ_MUL; v->flags = VIDEO_TUNER_LOW; v->mode = VIDEO_MODE_AUTO; v->signal = radio->stereo*0x7000; @@ -268,31 +281,31 @@ static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file, radio->curfreq = *freq; if (dsbr100_setfreq(radio, radio->curfreq)==-1) - warn("set frequency failed"); + warn("Set frequency failed"); return 0; } case VIDIOCGAUDIO: { struct video_audio *v = arg; - memset(v,0, sizeof(*v)); - v->flags|=VIDEO_AUDIO_MUTABLE; - v->mode=VIDEO_SOUND_STEREO; - v->volume=1; - v->step=1; + memset(v, 0, sizeof(*v)); + v->flags |= VIDEO_AUDIO_MUTABLE; + v->mode = VIDEO_SOUND_STEREO; + v->volume = 1; + v->step = 1; strcpy(v->name, "Radio"); return 0; } case VIDIOCSAUDIO: { struct video_audio *v = arg; - if(v->audio) + if (v->audio) return -EINVAL; - if(v->flags&VIDEO_AUDIO_MUTE) { + if (v->flags&VIDEO_AUDIO_MUTE) { if (dsbr100_stop(radio)==-1) - warn("radio did not respond properly"); + warn("Radio did not respond properly"); } else if (dsbr100_start(radio)==-1) - warn("radio did not respond properly"); + warn("Radio did not respond properly"); return 0; } default: @@ -312,18 +325,18 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) usb_dsbr100 *radio=dev->priv; if (! radio) { - warn("radio not initialised"); + warn("Radio not initialised"); return -EAGAIN; } if(users) { - warn("radio in use"); + warn("Radio in use"); return -EBUSY; } users++; if (dsbr100_start(radio)<0) - warn("radio did not start up properly"); - dsbr100_setfreq(radio,radio->curfreq); + warn("Radio did not start up properly"); + dsbr100_setfreq(radio, radio->curfreq); return 0; } @@ -335,7 +348,6 @@ static int usb_dsbr100_close(struct inode *inode, struct file *file) if (!radio) return -ENODEV; users--; - dsbr100_stop(radio); return 0; } @@ -343,8 +355,9 @@ static int __init dsbr100_init(void) { usb_dsbr100_radio.priv = NULL; usb_register(&usb_dsbr100_driver); - if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO,radio_nr)==-1) { - warn("couldn't register video device"); + if (video_register_device(&usb_dsbr100_radio, VFL_TYPE_RADIO, + radio_nr)==-1) { + warn("Couldn't register video device"); return -EINVAL; } info(DRIVER_VERSION ":" DRIVER_DESC); @@ -367,9 +380,3 @@ module_exit (dsbr100_exit); MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); - -/* -vi: ts=8 -Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is -my command. -*/ -- cgit v1.2.3 From 6f224290f1af345d4636c0b646c979e4144de51b Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Thu, 13 Mar 2003 19:10:11 -0800 Subject: [PATCH] USB: more Edgeport USB Serial Converter driver stuff --- drivers/usb/serial/io_ti.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7ba4d95425d4..4562b03c1a33 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1151,8 +1151,12 @@ static int TIDownloadFirmware (struct edgeport_serial *serial) dbg ( "%s - HARDWARE RESET return %d", __FUNCTION__, status); /* return an error on purpose. */ + kfree (firmware_version); + kfree (rom_desc); + kfree (ti_manuf_desc); return -ENODEV; } + kfree (firmware_version); } // Search for type 0xF2 record (firmware blank record) else if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) { -- cgit v1.2.3 From cb2d3c24a392afd61fadfa6fc78e1e761e4bf3e4 Mon Sep 17 00:00:00 2001 From: Steven Cole Date: Thu, 13 Mar 2003 19:58:58 -0800 Subject: [PATCH] USB: spelling fixes for drivers/usb This spelling and typo cleanup patch was reviewed by Mike Hayes and Jared Daniel J. Smith. --- drivers/usb/class/bluetty.c | 4 ++-- drivers/usb/class/cdc-acm.c | 4 ++-- drivers/usb/class/usb-midi.c | 2 +- drivers/usb/core/hub.c | 4 ++-- drivers/usb/core/urb.c | 2 +- drivers/usb/host/hc_simple.c | 2 +- drivers/usb/host/hc_simple.h | 4 ++-- drivers/usb/host/hc_sl811.h | 2 +- drivers/usb/image/hpusbscsi.c | 4 ++-- drivers/usb/image/hpusbscsi.h | 2 +- drivers/usb/image/scanner.c | 6 +++--- drivers/usb/input/hid-core.c | 2 +- drivers/usb/input/hid-input.c | 2 +- drivers/usb/input/hid.h | 6 +++--- drivers/usb/media/konicawc.c | 2 +- drivers/usb/media/pwc-ctrl.c | 2 +- drivers/usb/media/pwc-if.c | 6 +++--- drivers/usb/media/pwc-uncompress.c | 2 +- drivers/usb/media/pwc.h | 2 +- drivers/usb/media/se401.c | 2 +- drivers/usb/media/vicam.c | 2 +- drivers/usb/misc/auerswald.c | 16 ++++++++-------- drivers/usb/misc/speedtouch.c | 2 +- drivers/usb/net/usbnet.c | 2 +- drivers/usb/serial/belkin_sa.c | 2 +- drivers/usb/serial/cyberjack.c | 2 +- drivers/usb/serial/io_edgeport.c | 2 +- drivers/usb/serial/io_ionsp.h | 2 +- drivers/usb/serial/ir-usb.c | 4 ++-- drivers/usb/serial/keyspan_usa26msg.h | 2 +- drivers/usb/serial/keyspan_usa28msg.h | 2 +- drivers/usb/serial/keyspan_usa49msg.h | 2 +- drivers/usb/serial/usb-serial.c | 4 ++-- drivers/usb/serial/usb-serial.h | 2 +- drivers/usb/storage/isd200.c | 6 +++--- drivers/usb/storage/transport.h | 2 +- drivers/usb/usb-skeleton.c | 2 +- 37 files changed, 59 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c index d7cc97c9eff1..b83068f9f13a 100644 --- a/drivers/usb/class/bluetty.c +++ b/drivers/usb/class/bluetty.c @@ -168,7 +168,7 @@ struct usb_bluetooth { int magic; struct usb_device * dev; struct tty_driver * tty_driver; /* the tty_driver for this device */ - struct tty_struct * tty; /* the coresponding tty for this port */ + struct tty_struct * tty; /* the corresponding tty for this port */ unsigned char minor; /* the starting minor number for this device */ int throttle; /* throttled by tty layer */ @@ -1328,7 +1328,7 @@ int usb_bluetooth_init(void) int i; int result; - /* Initalize our global data */ + /* Initialize our global data */ for (i = 0; i < BLUETOOTH_TTY_MINORS; ++i) { bluetooth_table[i] = NULL; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5cd38d761590..7a6b3d0204e8 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -138,9 +138,9 @@ struct acm_line { */ struct acm { - struct usb_device *dev; /* the coresponding usb device */ + struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *iface; /* the interfaces - +0 control +1 data */ - struct tty_struct *tty; /* the coresponding tty */ + struct tty_struct *tty; /* the corresponding tty */ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ struct acm_line line; /* line coding (bits, stop, parity) */ struct work_struct work; /* work queue entry for line discipline waking up */ diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index 4e6d970299da..1361485d7629 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -1270,7 +1270,7 @@ static void *find_descriptor( void *descStart, unsigned int descLength, void *af return NULL; } -/** Utility to find a class-specfic interface descriptor. +/** Utility to find a class-specific interface descriptor. * dsubtype is a descriptor subtype * Called by parseDescriptor(); **/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index cd039053c127..b8f282d5524a 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -516,7 +516,7 @@ descriptor_error: endpoint = &desc->endpoint[0].desc; - /* Output endpoint? Curiousier and curiousier.. */ + /* Output endpoint? Curiouser and curiouser.. */ if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { goto descriptor_error; } @@ -958,7 +958,7 @@ static void usb_hub_events(void) int i, ret; /* - * We restart the list everytime to avoid a deadlock with + * We restart the list every time to avoid a deadlock with * deleting hubs downstream from this one. This should be * safe since we delete the hub from the event list. * Not the most efficient, but avoids deadlocks. diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 18b44d3cd9a7..8b2624844cb1 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -69,7 +69,7 @@ void usb_free_urb(struct urb *urb) * usb_get_urb - increments the reference count of the urb * @urb: pointer to the urb to modify * - * This must be called whenever a urb is transfered from a device driver to a + * This must be called whenever a urb is transferred from a device driver to a * host controller driver. This allows proper reference counting to happen * for urbs. * diff --git a/drivers/usb/host/hc_simple.c b/drivers/usb/host/hc_simple.c index 5d4cf5c02971..f970656f0810 100644 --- a/drivers/usb/host/hc_simple.c +++ b/drivers/usb/host/hc_simple.c @@ -605,7 +605,7 @@ static struct urb *qu_return_urb (hci_t * hci, struct urb * urb, int resub_ok) /*************************************************************************** * Function Name : sh_scan_iso_urb_list * - * This function goes throught the isochronous urb list and schedule the + * This function goes through the isochronous urb list and schedule the * the transfer. * * Note: This function has not tested yet diff --git a/drivers/usb/host/hc_simple.h b/drivers/usb/host/hc_simple.h index dfb67e7d964e..d0289f62f058 100644 --- a/drivers/usb/host/hc_simple.h +++ b/drivers/usb/host/hc_simple.h @@ -44,8 +44,8 @@ struct virt_root_hub { int devnum; /* Address of Root Hub endpoint */ void *urb; /* interrupt URB of root hub */ int send; /* active flag */ - int interval; /* intervall of roothub interrupt transfers */ - struct timer_list rh_int_timer; /* intervall timer for rh interrupt EP */ + int interval; /* interval of roothub interrupt transfers */ + struct timer_list rh_int_timer; /* interval timer for rh interrupt EP */ }; #if 1 diff --git a/drivers/usb/host/hc_sl811.h b/drivers/usb/host/hc_sl811.h index 1618d233f236..8b9eed235088 100644 --- a/drivers/usb/host/hc_sl811.h +++ b/drivers/usb/host/hc_sl811.h @@ -368,7 +368,7 @@ struct hci; #define SL11H_CTL1VAL_RESET 8 -/* Interrut enable (addr 6) and interrupt status register bits (addr 0xD) */ +/* Interrupt enable (addr 6) and interrupt status register bits (addr 0xD) */ #define SL11H_INTMASK_XFERDONE 1 #define SL11H_INTMASK_SOFINTR 0x10 #define SL11H_INTMASK_INSRMV 0x20 diff --git a/drivers/usb/image/hpusbscsi.c b/drivers/usb/image/hpusbscsi.c index 9590371e8203..1b74264ee8e5 100644 --- a/drivers/usb/image/hpusbscsi.c +++ b/drivers/usb/image/hpusbscsi.c @@ -303,7 +303,7 @@ static int hpusbscsi_scsi_queuecommand (Scsi_Cmnd *srb, scsi_callback callback) } else { usb_callback = simple_payload_callback; } - /* Now we find out which direction data is to be transfered in */ + /* Now we find out which direction data is to be transferred in */ hpusbscsi->current_data_pipe = DIRECTION_IS_IN(srb->cmnd[0]) ? usb_rcvbulkpipe(hpusbscsi->dev, hpusbscsi->ep_in) : @@ -410,7 +410,7 @@ DEBUG("Getting status byte %d \n",hpusbscsi->scsi_state_byte); } if (hpusbscsi->scallback != NULL && hpusbscsi->state == HP_STATE_WAIT && scsi_state != CHECK_CONDITION <<1 ) - /* we do a callback to the scsi layer if and only if all data has been transfered */ + /* we do a callback to the scsi layer if and only if all data has been transferred */ hpusbscsi->scallback(hpusbscsi->srb); TRACE_STATE; diff --git a/drivers/usb/image/hpusbscsi.h b/drivers/usb/image/hpusbscsi.h index 6e83344dcaa9..b56f091cdd39 100644 --- a/drivers/usb/image/hpusbscsi.h +++ b/drivers/usb/image/hpusbscsi.h @@ -85,7 +85,7 @@ static Scsi_Host_Template hpusbscsi_scsi_host_template = { /* defines for internal driver state */ #define HP_STATE_FREE 0 /*ready for next request */ -#define HP_STATE_BEGINNING 1 /*command being transfered */ +#define HP_STATE_BEGINNING 1 /*command being transferred */ #define HP_STATE_WORKING 2 /* data transfer stage */ #define HP_STATE_ERROR 3 /* error has been reported */ #define HP_STATE_WAIT 4 /* waiting for status transfer */ diff --git a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c index f1d758d78cf7..6fb7c46362d6 100644 --- a/drivers/usb/image/scanner.c +++ b/drivers/usb/image/scanner.c @@ -33,8 +33,8 @@ * 0.1 8/31/1999 * * Developed/tested using linux-2.3.15 with minor ohci.c changes to - * support short packes during bulk xfer mode. Some testing was - * done with ohci-hcd but the performace was low. Very limited + * support short packets during bulk xfer mode. Some testing was + * done with ohci-hcd but the performance was low. Very limited * testing was performed with uhci but I was unable to get it to * work. Initial relase to the linux-usb development effort. * @@ -338,7 +338,7 @@ * Till Kamppeter and others for all the ids. * - Cleaned up list of vendor/product ids. * - Print information about user-supplied ids only once at startup instead - * of everytime any USB device is plugged in. + * of every time any USB device is plugged in. * - Removed PV8630 ioctls. Use the standard ioctls instead. * - Made endpoint detection more generic. Basically, only one bulk-in * endpoint is required, everything else is optional. diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index 0b222dd524c1..4da30222593a 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -954,7 +954,7 @@ void hid_output_report(struct hid_report *report, __u8 *data) /* * Set a field value. The report this field belongs to has to be - * created and transfered to the device, to set this value in the + * created and transferred to the device, to set this value in the * device. */ diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index c614338be50f..da0267893d06 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -480,7 +480,7 @@ static void hidinput_close(struct input_dev *dev) /* * Register the input device; print a message. * Configure the input layer interface - * Read all reports and initalize the absoulte field values. + * Read all reports and initialize the absolute field values. */ int hidinput_connect(struct hid_device *hid) diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h index 70cab1643aa7..a6081d795493 100644 --- a/drivers/usb/input/hid.h +++ b/drivers/usb/input/hid.h @@ -209,8 +209,8 @@ struct hid_item { #define HID_QUIRK_BADPAD 0x20 /* - * This is the global enviroment of the parser. This information is - * persistent for main-items. The global enviroment can be saved and + * This is the global environment of the parser. This information is + * persistent for main-items. The global environment can be saved and * restored with PUSH/POP statements. */ @@ -228,7 +228,7 @@ struct hid_global { }; /* - * This is the local enviroment. It is resistent up the next main-item. + * This is the local environment. It is persistent up the next main-item. */ #define HID_MAX_DESCRIPTOR_SIZE 4096 diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 90049bc8993a..b79f0e816d4d 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -65,7 +65,7 @@ static const int debug = 0; #endif -/* Some default values for inital camera settings, +/* Some default values for initial camera settings, can be set by modprobe */ static enum frame_sizes size; diff --git a/drivers/usb/media/pwc-ctrl.c b/drivers/usb/media/pwc-ctrl.c index 92c5c8239913..a1af23e7b589 100644 --- a/drivers/usb/media/pwc-ctrl.c +++ b/drivers/usb/media/pwc-ctrl.c @@ -129,7 +129,7 @@ static struct Nala_table_entry Nala_table[PSZ_MAX][8] = 4 compression modi: none, low, medium, high When an uncompressed mode is not available, the next available compressed mode - will be choosen (unless the decompressor is absent). Sometimes there are only + will be chosen (unless the decompressor is absent). Sometimes there are only 1 or 2 compressed modes available; in that case entries are duplicated. */ struct Timon_table_entry diff --git a/drivers/usb/media/pwc-if.c b/drivers/usb/media/pwc-if.c index 6414b56defa4..19fe16084b90 100644 --- a/drivers/usb/media/pwc-if.c +++ b/drivers/usb/media/pwc-if.c @@ -255,7 +255,7 @@ static int pwc_allocate_buffers(struct pwc_device *pdev) return -ENXIO; } #endif - /* Allocate Isochronuous pipe buffers */ + /* Allocate Isochronous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { kbuf = kmalloc(ISO_BUFFER_SIZE, GFP_KERNEL); @@ -811,7 +811,7 @@ static int pwc_isoc_init(struct pwc_device *pdev) if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) { Err("Failed to find packet size for video endpoint in current alternate setting.\n"); - return -ENFILE; /* Odd error, that should be noticable */ + return -ENFILE; /* Odd error, that should be noticeable */ } /* Set alternate interface */ @@ -2040,7 +2040,7 @@ static int __init usb_pwc_init(void) if (leds[1] >= 0) led_off = leds[1]; - /* Big device node whoopla. Basicly, it allows you to assign a + /* Big device node whoopla. Basically, it allows you to assign a device node (/dev/videoX) to a camera, based on its type & serial number. The format is [type[.serialnumber]:]node. diff --git a/drivers/usb/media/pwc-uncompress.c b/drivers/usb/media/pwc-uncompress.c index b34074e5d40d..42a65168dd5d 100644 --- a/drivers/usb/media/pwc-uncompress.c +++ b/drivers/usb/media/pwc-uncompress.c @@ -32,7 +32,7 @@ static LIST_HEAD(pwc_decompressor_list); /* Should the pwc_decompress structure ever change, we increase the version number so that we don't get nasty surprises, or can - dynamicly adjust our structure. + dynamically adjust our structure. */ const int pwc_decompressor_version = PWC_MAJOR; diff --git a/drivers/usb/media/pwc.h b/drivers/usb/media/pwc.h index 3ff42fc9f20e..9745effa4407 100644 --- a/drivers/usb/media/pwc.h +++ b/drivers/usb/media/pwc.h @@ -147,7 +147,7 @@ struct pwc_device 2. data is synchronized and packed into a frame buffer 3a. in case data is compressed, decompress it directly into image buffer 3b. in case data is uncompressed, copy into image buffer with viewport - 4. data is transfered to the user process + 4. data is transferred to the user process Note that MAX_ISO_BUFS != MAX_FRAMES != MAX_IMAGES.... We have in effect a back-to-back-double-buffer system. diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index cde8b307a2a1..f1f3210f3a48 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -692,7 +692,7 @@ static int se401_set_size(struct usb_se401 *se401, int width, int height) /* This shouldn't really be done in a v4l driver.... But it does make the image look a lot more usable. - Basicly it lifts the dark pixels more than the light pixels. + Basically it lifts the dark pixels more than the light pixels. */ static inline void enhance_picture(unsigned char *frame, int len) { diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 831d932ea9e5..adb87516e8ab 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -531,7 +531,7 @@ vicam_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsign return -ENODEV; switch (ioctlnr) { - /* query capabilites */ + /* query capabilities */ case VIDIOCGCAP: { struct video_capability b; diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index 28132e35a4a7..74ca6cacd589 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -687,10 +687,10 @@ static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int time This function sends a simple control message to a specified endpoint and waits for the message to complete, or timeout. - If successful, it returns the transfered length, othwise a negative error number. + If successful, it returns the transferred length, otherwise a negative error number. Don't use this function from within an interrupt context, like a - bottom half handler. If you need a asyncronous message, or need to send + bottom half handler. If you need an asynchronous message, or need to send a message from within interrupt context, use auerchain_submit_urb() */ static int auerchain_control_msg (pauerchain_t acp, struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, @@ -814,7 +814,7 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned } return 0; -bl_fail:/* not enought memory. Free allocated elements */ +bl_fail:/* not enough memory. Free allocated elements */ dbg ("auerbuf_setup: no more memory"); auerbuf_free_buffers (bcp); return -ENOMEM; @@ -1083,7 +1083,7 @@ static void auerswald_int_complete (struct urb * urb, struct pt_regs *regs) /* can we do something more? This is a big problem: if this int packet is ignored, the device will wait forever and not signal any more data. - The only real solution is: having enought buffers! + The only real solution is: having enough buffers! Or perhaps temporary disabling the int endpoint? */ goto exit; @@ -1130,7 +1130,7 @@ static void auerswald_int_free (pauerswald_t cp) } /* This function is called to activate the interrupt - endpoint. This function returns 0 if successfull or an error code. + endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ static int auerswald_int_open (pauerswald_t cp) @@ -1181,7 +1181,7 @@ intoend: } /* This function is called to deactivate the interrupt - endpoint. This function returns 0 if successfull or an error code. + endpoint. This function returns 0 if successful or an error code. NOTE: no mutex please! */ static int auerswald_int_release (pauerswald_t cp) @@ -1713,7 +1713,7 @@ doreadlist: return -ERESTARTSYS; } - /* try to read the incomming data again */ + /* try to read the incoming data again */ goto doreadlist; } @@ -2075,7 +2075,7 @@ pfail: auerswald_delete (cp); The argument dev specifies the device context and the driver_context returns a pointer to the previously registered driver_context of the probe function. After returning from the disconnect function the USB - framework completly deallocates all data structures associated with + framework completely deallocates all data structures associated with this device. So especially the usb_device structure must not be used any longer by the usb driver. */ diff --git a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c index 0a6a227084f1..58bb47c64e6a 100644 --- a/drivers/usb/misc/speedtouch.c +++ b/drivers/usb/misc/speedtouch.c @@ -193,7 +193,7 @@ struct udsl_instance_data { static const char udsl_driver_name [] = "speedtch"; /* - * atm driver prototypes and stuctures + * atm driver prototypes and structures */ static void udsl_atm_dev_close (struct atm_dev *dev); diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 398aa567d6ba..fec152c2f241 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -1707,7 +1707,7 @@ static int usbnet_stop (struct net_device *net) /*-------------------------------------------------------------------------*/ -// posts reads, and enables write queing +// posts reads, and enables write queuing // precondition: never called in_interrupt diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 7cf6b5847c1c..0c790ae6a4e8 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -215,7 +215,7 @@ static int belkin_sa_open (struct usb_serial_port *port, struct file *filp) dbg("%s port %d", __FUNCTION__, port->number); /*Start reading from the device*/ - /* TODO: Look at possibility of submitting mulitple URBs to device to + /* TODO: Look at possibility of submitting multiple URBs to device to * enhance buffering. Win trace shows 16 initial read URBs. */ port->read_urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 8f6dcd730173..b0161705047e 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -302,7 +302,7 @@ static void cyberjack_read_int_callback( struct urb *urb, struct pt_regs *regs ) short old_rdtodo = priv->rdtodo; int result; - /* This is a announcement of comming bulk_ins. */ + /* This is a announcement of coming bulk_ins. */ unsigned short size = ((unsigned short)data[3]<<8)+data[2]+3; if( (size>259) || (size==0) ) { diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index af4a621ff524..37e266f52650 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1086,7 +1086,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) return -ENODEV; } - /* now wait for the port to be completly opened */ + /* now wait for the port to be completely opened */ timeout = OPEN_TIMEOUT; while (timeout && edge_port->openPending == TRUE) { timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout); diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h index e5c344d033ec..944fc04f1946 100644 --- a/drivers/usb/serial/io_ionsp.h +++ b/drivers/usb/serial/io_ionsp.h @@ -19,7 +19,7 @@ The data to and from all ports on the peripheral is multiplexed through a single endpoint pair (EP1 since it supports 64-byte MaxPacketSize). Therefore, the data, commands, and status for -each port must be preceeded by a short header identifying the +each port must be preceded by a short header identifying the destination port. The header also identifies the bytes that follow as data or as command/status info. diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 9381bc4f6023..332a56327123 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -471,7 +471,7 @@ static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs) /* * Bypass flip-buffers, and feed the ldisc directly - * due to our potentally large buffer size. Since we + * due to our potentially large buffer size. Since we * used to set low_latency, this is exactly what the * tty layer did anyway :) */ @@ -553,7 +553,7 @@ static void ir_set_termios (struct usb_serial_port *port, struct termios *old_te /* * FIXME, we should compare the baud request against the * capability stated in the IR header that we got in the - * startup funtion. + * startup function. */ switch (cflag & CBAUD) { case B2400: ir_baud = SPEED_2400; break; diff --git a/drivers/usb/serial/keyspan_usa26msg.h b/drivers/usb/serial/keyspan_usa26msg.h index d3c0cb0277b9..cac3d501f523 100644 --- a/drivers/usb/serial/keyspan_usa26msg.h +++ b/drivers/usb/serial/keyspan_usa26msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/keyspan_usa28msg.h b/drivers/usb/serial/keyspan_usa28msg.h index 4641abd7d682..d76eb72878e1 100644 --- a/drivers/usb/serial/keyspan_usa28msg.h +++ b/drivers/usb/serial/keyspan_usa28msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/keyspan_usa49msg.h b/drivers/usb/serial/keyspan_usa49msg.h index 73444c00023d..320b2cc76874 100644 --- a/drivers/usb/serial/keyspan_usa49msg.h +++ b/drivers/usb/serial/keyspan_usa49msg.h @@ -23,7 +23,7 @@ notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The name of InnoSys Incorprated may not be used to endorse or promote + 3. The name of InnoSys Incorporated may not be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 6eda0113534b..c57ce2d5f31f 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -229,7 +229,7 @@ * (01/23/2000) gkh * Fixed problem of crash when trying to open a port that didn't have a * device assigned to it. Made the minor node finding a little smarter, - * now it looks to find a continous space for the new device. + * now it looks to find a continuous space for the new device. * * (01/21/2000) gkh * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) @@ -1293,7 +1293,7 @@ static int __init usb_serial_init(void) int i; int result = 0; - /* Initalize our global data */ + /* Initialize our global data */ for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index 5389343ecaf7..ad2d292ddf53 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -71,7 +71,7 @@ * usb_serial_port: structure for the specific ports of a device. * @magic: magic number for internal validity of this pointer. * @serial: pointer back to the struct usb_serial owner of this port. - * @tty: pointer to the coresponding tty for this port. + * @tty: pointer to the corresponding tty for this port. * @number: the number of the port (the minor number). * @interrupt_in_buffer: pointer to the interrupt in buffer for this port. * @interrupt_in_urb: pointer to the interrupt in struct urb for this port. diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 9a88b60c9bd5..1f56f63bb8e5 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -650,7 +650,7 @@ static void isd200_log_config( struct isd200_info* info ) /************************************************************************** * isd200_write_config * - * Write the ISD200 Configuraton data + * Write the ISD200 Configuration data * * RETURNS: * ISD status code @@ -693,7 +693,7 @@ int isd200_write_config( struct us_data *us ) /************************************************************************** * isd200_read_config * - * Reads the ISD200 Configuraton data + * Reads the ISD200 Configuration data * * RETURNS: * ISD status code @@ -977,7 +977,7 @@ int isd200_get_inquiry_data( struct us_data *us ) /* check for an ATA device */ if (info->DeviceFlags & DF_ATA_DEVICE) { /* this must be an ATA device */ - /* perform an ATA Commmand Identify */ + /* perform an ATA Command Identify */ transferStatus = isd200_action( us, ACTION_IDENTIFY, &info->drive, sizeof(struct hd_driveid) ); diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 799c4134751e..38c38ee90011 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -118,7 +118,7 @@ struct bulk_cs_wrap { */ #define USB_STOR_XFER_GOOD 0 /* good transfer */ -#define USB_STOR_XFER_SHORT 1 /* transfered less than expected */ +#define USB_STOR_XFER_SHORT 1 /* transferred less than expected */ #define USB_STOR_XFER_STALLED 2 /* endpoint stalled */ #define USB_STOR_XFER_ERROR 3 /* transfer died in the middle */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index b5c7e6745b1a..b89ca760a959 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -64,7 +64,7 @@ #define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com" #define DRIVER_DESC "USB Skeleton Driver" -/* Module paramaters */ +/* Module parameters */ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug enabled or not"); -- cgit v1.2.3 From 169b4d42b6a8a4a31284d7f1ee03ceee27d16f05 Mon Sep 17 00:00:00 2001 From: Oleg Drokin Date: Thu, 13 Mar 2003 20:05:46 -0800 Subject: [PATCH] USB: memleak in Edgeport USB Serial Converter driver --- drivers/usb/serial/io_ti.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 4562b03c1a33..7849f372f916 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -468,7 +468,7 @@ static int TIIsTxActive (struct edgeport_port *port) { int status; struct out_endpoint_desc_block *oedb; - __u8 lsr; + __u8 *lsr; int bytes_left = 0; oedb = kmalloc (sizeof (* oedb), GFP_KERNEL); @@ -477,6 +477,13 @@ static int TIIsTxActive (struct edgeport_port *port) return -ENOMEM; } + lsr = kmalloc (1, GFP_KERNEL); /* Sigh, that's right, just one byte, + as not all platforms can do DMA + from stack */ + if (!lsr) { + kfree(oedb); + return -ENOMEM; + } /* Read the DMA Count Registers */ status = TIReadRam (port->port->serial->dev, port->dma_address, @@ -492,7 +499,7 @@ static int TIIsTxActive (struct edgeport_port *port) status = TIReadRam (port->port->serial->dev, port->uart_base + UMPMEM_OFFS_UART_LSR, 1, - &lsr); + lsr); if (status) goto exit_is_tx_active; @@ -508,6 +515,9 @@ static int TIIsTxActive (struct edgeport_port *port) /* We return Not Active if we get any kind of error */ exit_is_tx_active: dbg ("%s - return %d", __FUNCTION__, bytes_left ); + + kfree(lsr); + kfree(oedb); return bytes_left; } -- cgit v1.2.3 From 1bed90b338d93e1607efde841b6a26375cd18699 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Mar 2003 20:06:50 -0800 Subject: [PATCH] USB: fixup from previous io_ti.c patch --- drivers/usb/serial/io_ti.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 7849f372f916..b2368748b9de 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -503,13 +503,13 @@ static int TIIsTxActive (struct edgeport_port *port) if (status) goto exit_is_tx_active; - dbg ("%s - LSR = 0x%X", __FUNCTION__, lsr); + dbg ("%s - LSR = 0x%X", __FUNCTION__, *lsr); /* If either buffer has data or we are transmitting then return TRUE */ if ((oedb->XByteCount & 0x80 ) != 0 ) bytes_left += 64; - if ((lsr & UMP_UART_LSR_TX_MASK ) == 0 ) + if ((*lsr & UMP_UART_LSR_TX_MASK ) == 0 ) bytes_left += 1; /* We return Not Active if we get any kind of error */ -- cgit v1.2.3 From cc1dd8e2c1a9086ecffe559447cc8242e28a6d9d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 13 Mar 2003 23:06:18 -0800 Subject: [PATCH] USB: added support for Ericsson data cable to pl2303 driver. Thanks to kai.engert@gmx.de for the needed information --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 96f5c051c8e0..b341aad16a6e 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -76,6 +76,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) }, + { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 63051e66e69c..5e2abcc0db20 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -34,3 +34,6 @@ #define RADIOSHACK_VENDOR_ID 0x1453 #define RADIOSHACK_PRODUCT_ID 0x4026 + +#define DCU10_VENDOR_ID 0x0731 +#define DCU10_PRODUCT_ID 0x0528 -- cgit v1.2.3 From 8cdeea1a383b6b7d2ca2dfe3fd786f24d59bdc99 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:08:13 -0800 Subject: [PATCH] md: Missing mddev_put in md resync code Whenever a ITERATE_MDDEV loop is exitted abnormally we need to mddev_put the current mddev. There was one point in md_do_sync where we didn't so use counts became wrong. --- drivers/md/md.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index cd65d8960c39..03ee64e3d746 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2920,8 +2920,10 @@ static void md_do_sync(void *data) goto skip; } } - if (mddev->curr_resync == 1) + if (mddev->curr_resync == 1) { + mddev_put(mddev2); break; + } } } while (mddev->curr_resync < 2); -- cgit v1.2.3 From d7710d00ef73d87efc610cbacdeac202947b384a Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:08:21 -0800 Subject: [PATCH] md: Convert /proc/mdstat to use seq_file From: Angus Sawyer Mainly straightforward convert of sprintf -> seq_printf. seq_start and seq_next modelled on /proc/partitions. locking/ref counting as for ITERATE_MDDEV. pos == 0 -> header pos == n -> nth mddev pos == 0x10000 -> tail --- drivers/md/linear.c | 16 ++-- drivers/md/md.c | 191 +++++++++++++++++++++++++++++++++------------- drivers/md/multipath.c | 11 ++- drivers/md/raid0.c | 25 +++--- drivers/md/raid1.c | 11 ++- drivers/md/raid5.c | 15 ++-- include/linux/raid/md.h | 1 + include/linux/raid/md_k.h | 2 +- 8 files changed, 176 insertions(+), 96 deletions(-) (limited to 'drivers') diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 1c94ad6ff2a5..380b28ce026b 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -208,31 +208,29 @@ static int linear_make_request (request_queue_t *q, struct bio *bio) return 1; } -static int linear_status (char *page, mddev_t *mddev) +static void linear_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j; linear_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page+sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page+sz, "[%s", + seq_printf(seq, "[%s", bdev_partition_name(conf->hash_table[j].dev0->rdev->bdev)); if (conf->hash_table[j].dev1) - sz += sprintf(page+sz, "/%s] ", + seq_printf(seq, "/%s] ", bdev_partition_name(conf->hash_table[j].dev1->rdev->bdev)); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); #endif - sz += sprintf(page+sz, " %dk rounding", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk rounding", mddev->chunk_size/1024); } diff --git a/drivers/md/md.c b/drivers/md/md.c index 03ee64e3d746..f57b98e067cc 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2621,30 +2621,30 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) md_recover_arrays(); } -static int status_unused(char * page) +/* seq_file implementation /proc/mdstat */ + +static void status_unused(struct seq_file *seq) { - int sz = 0, i = 0; + int i = 0; mdk_rdev_t *rdev; struct list_head *tmp; - sz += sprintf(page + sz, "unused devices: "); + seq_printf(seq, "unused devices: "); ITERATE_RDEV_PENDING(rdev,tmp) { i++; - sz += sprintf(page + sz, "%s ", + seq_printf(seq, "%s ", bdev_partition_name(rdev->bdev)); } if (!i) - sz += sprintf(page + sz, ""); + seq_printf(seq, ""); - sz += sprintf(page + sz, "\n"); - return sz; + seq_printf(seq, "\n"); } -static int status_resync(char * page, mddev_t * mddev) +static void status_resync(struct seq_file *seq, mddev_t * mddev) { - int sz = 0; unsigned long max_blocks, resync, res, dt, db, rt; resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; @@ -2655,20 +2655,20 @@ static int status_resync(char * page, mddev_t * mddev) */ if (!max_blocks) { MD_BUG(); - return 0; + return; } res = (resync/1024)*1000/(max_blocks/1024 + 1); { int i, x = res/50, y = 20-x; - sz += sprintf(page + sz, "["); + seq_printf(seq, "["); for (i = 0; i < x; i++) - sz += sprintf(page + sz, "="); - sz += sprintf(page + sz, ">"); + seq_printf(seq, "="); + seq_printf(seq, ">"); for (i = 0; i < y; i++) - sz += sprintf(page + sz, "."); - sz += sprintf(page + sz, "] "); + seq_printf(seq, "."); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, " %s =%3lu.%lu%% (%lu/%lu)", + seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)", (mddev->spares ? "recovery" : "resync"), res/10, res % 10, resync, max_blocks); @@ -2686,44 +2686,110 @@ static int status_resync(char * page, mddev_t * mddev) db = resync - (mddev->resync_mark_cnt/2); rt = (dt * ((max_blocks-resync) / (db/100+1)))/100; - sz += sprintf(page + sz, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + + seq_printf(seq, " speed=%ldK/sec", db/dt); +} + +static void *md_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct list_head *tmp; + loff_t l = *pos; + mddev_t *mddev; + + if (l > 0x10000) + return NULL; + if (!l--) + /* header */ + return (void*)1; + + spin_lock(&all_mddevs_lock); + list_for_each(tmp,&all_mddevs) + if (!l--) { + mddev = list_entry(tmp, mddev_t, all_mddevs); + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + return mddev; + } + spin_unlock(&all_mddevs_lock); + return (void*)2;/* tail */ +} + +static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *tmp; + mddev_t *next_mddev, *mddev = v; + + ++*pos; + if (v == (void*)2) + return NULL; + + spin_lock(&all_mddevs_lock); + if (v == (void*)1) + tmp = all_mddevs.next; + else + tmp = mddev->all_mddevs.next; + if (tmp != &all_mddevs) + next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs)); + else { + next_mddev = (void*)2; + *pos = 0x10000; + } + spin_unlock(&all_mddevs_lock); + + if (v != (void*)1) + mddev_put(mddev); + return next_mddev; + +} - sz += sprintf(page + sz, " speed=%ldK/sec", db/dt); +static void md_seq_stop(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; - return sz; + if (mddev && v != (void*)1 && v != (void*)2) + mddev_put(mddev); } -static int md_status_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int md_seq_show(struct seq_file *seq, void *v) { - int sz = 0, j; + mddev_t *mddev = v; sector_t size; - struct list_head *tmp, *tmp2; + struct list_head *tmp2; mdk_rdev_t *rdev; - mddev_t *mddev; + int i; - sz += sprintf(page + sz, "Personalities : "); - for (j = 0; j < MAX_PERSONALITY; j++) - if (pers[j]) - sz += sprintf(page+sz, "[%s] ", pers[j]->name); + if (v == (void*)1) { + seq_printf(seq, "Personalities : "); + for (i = 0; i < MAX_PERSONALITY; i++) + if (pers[i]) + seq_printf(seq, "[%s] ", pers[i]->name); - sz += sprintf(page+sz, "\n"); + seq_printf(seq, "\n"); + return 0; + } + if (v == (void*)2) { + status_unused(seq); + return 0; + } - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { - sz += sprintf(page + sz, "md%d : %sactive", mdidx(mddev), + if (mddev_lock(mddev)!=0) + return -EINTR; + if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) { + seq_printf(seq, "md%d : %sactive", mdidx(mddev), mddev->pers ? "" : "in"); if (mddev->pers) { if (mddev->ro) - sz += sprintf(page + sz, " (read-only)"); - sz += sprintf(page + sz, " %s", mddev->pers->name); + seq_printf(seq, " (read-only)"); + seq_printf(seq, " %s", mddev->pers->name); } size = 0; ITERATE_RDEV(mddev,rdev,tmp2) { - sz += sprintf(page + sz, " %s[%d]", + seq_printf(seq, " %s[%d]", bdev_partition_name(rdev->bdev), rdev->desc_nr); if (rdev->faulty) { - sz += sprintf(page + sz, "(F)"); + seq_printf(seq, "(F)"); continue; } size += rdev->size; @@ -2731,34 +2797,50 @@ static int md_status_read_proc(char *page, char **start, off_t off, if (!list_empty(&mddev->disks)) { if (mddev->pers) - sz += sprintf(page + sz, "\n %llu blocks", + seq_printf(seq, "\n %llu blocks", (unsigned long long)md_size[mdidx(mddev)]); else - sz += sprintf(page + sz, "\n %llu blocks", (unsigned long long)size); + seq_printf(seq, "\n %llu blocks", (unsigned long long)size); } - if (!mddev->pers) { - sz += sprintf(page+sz, "\n"); - mddev_unlock(mddev); - continue; + if (mddev->pers) { + mddev->pers->status (seq, mddev); + seq_printf(seq, "\n "); + if (mddev->curr_resync > 2) + status_resync (seq, mddev); + else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) + seq_printf(seq, " resync=DELAYED"); } - sz += mddev->pers->status (page+sz, mddev); + seq_printf(seq, "\n"); + } + mddev_unlock(mddev); + + return 0; +} - sz += sprintf(page+sz, "\n "); - if (mddev->curr_resync > 2) - sz += status_resync (page+sz, mddev); - else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) - sz += sprintf(page + sz, " resync=DELAYED"); +static struct seq_operations md_seq_ops = { + .start = md_seq_start, + .next = md_seq_next, + .stop = md_seq_stop, + .show = md_seq_show, +}; - sz += sprintf(page + sz, "\n"); - mddev_unlock(mddev); - } - sz += status_unused(page + sz); +static int md_seq_open(struct inode *inode, struct file *file) +{ + int error; - return sz; + error = seq_open(file, &md_seq_ops); + return error; } +static struct file_operations md_seq_fops = { + .open = md_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + int register_md_personality(int pnum, mdk_personality_t *p) { if (pnum >= MAX_PERSONALITY) { @@ -3196,6 +3278,7 @@ struct notifier_block md_notifier = { static void md_geninit(void) { + struct proc_dir_entry *p; int i; for(i = 0; i < MAX_MD_DEVS; i++) { @@ -3205,7 +3288,9 @@ static void md_geninit(void) dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); #ifdef CONFIG_PROC_FS - create_proc_read_entry("mdstat", 0, NULL, md_status_read_proc, NULL); + p = create_proc_entry("mdstat", S_IRUGO, NULL); + if (p) + p->proc_fops = &md_seq_fops; #endif } diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 5b96e4a42d21..1eaa36356155 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -185,19 +185,18 @@ static int multipath_make_request (request_queue_t *q, struct bio * bio) return 0; } -static int multipath_status (char *page, mddev_t *mddev) +static void multipath_status (struct seq_file *seq, mddev_t *mddev) { multipath_conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->multipaths[i].rdev && conf->multipaths[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf (seq, "]"); } #define LAST_DISK KERN_ALERT \ diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 0d83f3647f93..00a79d9b364b 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -372,41 +372,40 @@ bad_zone1: return 0; } -static int raid0_status (char *page, mddev_t *mddev) +static void raid0_status (struct seq_file *seq, mddev_t *mddev) { - int sz = 0; #undef MD_DEBUG #ifdef MD_DEBUG int j, k; raid0_conf_t *conf = mddev_to_conf(mddev); - sz += sprintf(page + sz, " "); + seq_printf(seq, " "); for (j = 0; j < conf->nr_zones; j++) { - sz += sprintf(page + sz, "[z%d", + seq_printf(seq, "[z%d", conf->hash_table[j].zone0 - conf->strip_zone); if (conf->hash_table[j].zone1) - sz += sprintf(page+sz, "/z%d] ", + seq_printf(seq, "/z%d] ", conf->hash_table[j].zone1 - conf->strip_zone); else - sz += sprintf(page+sz, "] "); + seq_printf(seq, "] "); } - sz += sprintf(page + sz, "\n"); + seq_printf(seq, "\n"); for (j = 0; j < conf->nr_strip_zones; j++) { - sz += sprintf(page + sz, " z%d=[", j); + seq_printf(seq, " z%d=[", j); for (k = 0; k < conf->strip_zone[j].nb_dev; k++) - sz += sprintf (page+sz, "%s/", bdev_partition_name( + seq_printf (seq, "%s/", bdev_partition_name( conf->strip_zone[j].dev[k]->bdev)); - sz--; - sz += sprintf (page+sz, "] zo=%d do=%d s=%d\n", + + seq_printf (seq, "] zo=%d do=%d s=%d\n", conf->strip_zone[j].zone_offset, conf->strip_zone[j].dev_offset, conf->strip_zone[j].size); } #endif - sz += sprintf(page + sz, " %dk chunks", mddev->chunk_size/1024); - return sz; + seq_printf(seq, " %dk chunks", mddev->chunk_size/1024); + return; } static mdk_personality_t raid0_personality= diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 83ee73ef172f..152656ef5012 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -571,19 +571,18 @@ static int make_request(request_queue_t *q, struct bio * bio) return 0; } -static int status(char *page, mddev_t *mddev) +static void status(struct seq_file *seq, mddev_t *mddev) { conf_t *conf = mddev_to_conf(mddev); - int sz = 0, i; + int i; - sz += sprintf(page+sz, " [%d/%d] [", conf->raid_disks, + seq_printf(seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf(page+sz, "%s", + seq_printf(seq, "%s", conf->mirrors[i].rdev && conf->mirrors[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); - return sz; + seq_printf(seq, "]"); } #define LAST_DISK KERN_ALERT \ diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 7949de5f8e87..abec935d78d1 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1579,24 +1579,23 @@ static void printall (raid5_conf_t *conf) } #endif -static int status (char *page, mddev_t *mddev) +static void status (struct seq_file *seq, mddev_t *mddev) { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - int sz = 0, i; + int i; - sz += sprintf (page+sz, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); - sz += sprintf (page+sz, " [%d/%d] [", conf->raid_disks, conf->working_disks); + seq_printf (seq, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout); + seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks); for (i = 0; i < conf->raid_disks; i++) - sz += sprintf (page+sz, "%s", + seq_printf (seq, "%s", conf->disks[i].rdev && conf->disks[i].rdev->in_sync ? "U" : "_"); - sz += sprintf (page+sz, "]"); + seq_printf (seq, "]"); #if RAID5_DEBUG #define D(x) \ - sz += sprintf (page+sz, "<"#x":%d>", atomic_read(&conf->x)) + seq_printf (seq, "<"#x":%d>", atomic_read(&conf->x)) printall(conf); #endif - return sz; } static void print_raid5_conf (raid5_conf_t *conf) diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index d7834b08cc88..3260343d634e 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 6426a2c3fe8f..cf7505f655da 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -245,7 +245,7 @@ struct mdk_personality_s int (*make_request)(request_queue_t *q, struct bio *bio); int (*run)(mddev_t *mddev); int (*stop)(mddev_t *mddev); - int (*status)(char *page, mddev_t *mddev); + void (*status)(struct seq_file *seq, mddev_t *mddev); /* error_handler must set ->faulty and clear ->in_sync * if appropriate, and should abort recovery if needed */ -- cgit v1.2.3 From 07b24141669f233c3e9d5bb3459fb5646bdb1054 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:08:35 -0800 Subject: [PATCH] md: Opencode flush_curr_signals in md.c It is (now) too trivial to even have an inline. --- drivers/md/md.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index f57b98e067cc..36275a30ed8e 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2473,12 +2473,6 @@ static struct block_device_operations md_fops = .ioctl = md_ioctl, }; - -static inline void flush_curr_signals(void) -{ - flush_signals(current); -} - int md_thread(void * arg) { mdk_thread_t *thread = arg; @@ -2525,7 +2519,7 @@ int md_thread(void * arg) blk_run_queues(); } if (signal_pending(current)) - flush_curr_signals(); + flush_signals(current); } complete(thread->event); return 0; @@ -2945,7 +2939,7 @@ void md_handle_safemode(mddev_t *mddev) if (signal_pending(current)) { printk(KERN_INFO "md: md%d in safe mode\n",mdidx(mddev)); mddev->safemode= 1; - flush_curr_signals(); + flush_signals(current); } if (mddev->safemode) md_enter_safemode(mddev); @@ -2996,7 +2990,7 @@ static void md_do_sync(void *data) } if (wait_event_interruptible(resync_wait, mddev2->curr_resync < mddev->curr_resync)) { - flush_curr_signals(); + flush_signals(current); err = -EINTR; mddev_put(mddev2); goto skip; @@ -3079,7 +3073,7 @@ static void md_do_sync(void *data) * got a signal, exit. */ printk(KERN_INFO "md: md_do_sync() got signal ... exiting\n"); - flush_curr_signals(); + flush_signals(current); err = -EINTR; goto out; } -- cgit v1.2.3 From cb129263c29145416deb93988deb2c54c84849f0 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:08:40 -0800 Subject: [PATCH] md: Tidy up recovery_running flags in md Md uses ->recovery_running and ->recovery_err to keep track of the status or recovery. This is rather ad hoc and race prone. This patch changes it to ->recovery which has bit flags for various states. --- drivers/md/md.c | 88 ++++++++++++++++++++++++----------------------- drivers/md/raid1.c | 5 ++- drivers/md/raid5.c | 5 ++- include/linux/raid/md_k.h | 22 +++++++----- 4 files changed, 63 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 36275a30ed8e..f1bce4d8bd7d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1593,8 +1593,7 @@ static int do_md_stop(mddev_t * mddev, int ro) if (mddev->pers) { if (mddev->sync_thread) { - if (mddev->recovery_running > 0) - mddev->recovery_running = -1; + set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; } @@ -2663,7 +2662,8 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev) seq_printf(seq, "] "); } seq_printf(seq, " %s =%3lu.%lu%% (%lu/%lu)", - (mddev->spares ? "recovery" : "resync"), + (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? + "resync" : "recovery"), res/10, res % 10, resync, max_blocks); /* @@ -2896,8 +2896,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) atomic_sub(blocks, &mddev->recovery_active); wake_up(&mddev->recovery_wait); if (!ok) { - mddev->recovery_error = -EIO; - mddev->recovery_running = -1; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); md_recover_arrays(); // stop recovery, signal do_sync .... } @@ -2927,7 +2926,8 @@ static inline void md_enter_safemode(mddev_t *mddev) { mddev_lock_uninterruptible(mddev); - if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && !mddev->recovery_running) { + if (mddev->safemode && !atomic_read(&mddev->writes_pending) && + !mddev->in_sync && mddev->recovery_cp == MaxSector) { mddev->in_sync = 1; md_update_sb(mddev); } @@ -2963,7 +2963,7 @@ static void md_do_sync(void *data) unsigned long last_check; /* just incase thread restarts... */ - if (mddev->recovery_running <= 0) + if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) return; /* we overload curr_resync somewhat here. @@ -3031,8 +3031,6 @@ static void md_do_sync(void *data) init_waitqueue_head(&mddev->recovery_wait); last_check = 0; - mddev->recovery_error = 0; - if (mddev->recovery_cp) printk(KERN_INFO "md: resuming recovery of md%d from checkpoint.\n", mdidx(mddev)); @@ -3053,6 +3051,10 @@ static void md_do_sync(void *data) last_check = j; + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery) || + test_bit(MD_RECOVERY_ERR, &mddev->recovery)) + break; + blk_run_queues(); repeat: @@ -3107,26 +3109,26 @@ static void md_do_sync(void *data) out: wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); - if (mddev->recovery_running < 0 && - !mddev->recovery_error && mddev->curr_resync > 2) - { - /* interrupted but no write errors */ - printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); - mddev->recovery_cp = mddev->curr_resync; - } - /* tell personality that we are finished */ mddev->pers->sync_request(mddev, max_sectors, 1); - skip: - mddev->curr_resync = 0; + if (err) - mddev->recovery_running = -1; - if (mddev->recovery_running > 0) - mddev->recovery_running = 0; - if (mddev->recovery_running == 0) - mddev->recovery_cp = MaxSector; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && + mddev->curr_resync > 2 && + mddev->curr_resync > mddev->recovery_cp) { + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { + printk(KERN_INFO "md: checkpointing recovery of md%d.\n", mdidx(mddev)); + mddev->recovery_cp = mddev->curr_resync; + } else + mddev->recovery_cp = MaxSector; + } + if (mddev->safemode) md_enter_safemode(mddev); + skip: + mddev->curr_resync = 0; + set_bit(MD_RECOVERY_DONE, &mddev->recovery); md_recover_arrays(); } @@ -3136,10 +3138,10 @@ static void md_do_sync(void *data) * action that might be needed. * It does not do any resync itself, but rather "forks" off other threads * to do that as needed. - * When it is determined that resync is needed, we set "->recovery_running" and - * create a thread at ->sync_thread. - * When the thread finishes it clears recovery_running (or sets an error) - * and wakeup up this thread which will reap the thread and finish up. + * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in + * "->recovery" and create a thread at ->sync_thread. + * When the thread finishes it sets MD_RECOVERY_DONE (and might set MD_RECOVERY_ERR) + * and wakeups up this thread which will reap the thread and finish up. * This thread also removes any faulty devices (with nr_pending == 0). * * The overall approach is: @@ -3160,31 +3162,32 @@ void md_do_recovery(void *data) dprintk(KERN_INFO "md: recovery thread got woken up ...\n"); ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { + int spares =0; if (!mddev->raid_disks || !mddev->pers || mddev->ro) goto unlock; if (mddev->sb_dirty) md_update_sb(mddev); - if (mddev->recovery_running > 0) + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && + !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) /* resync/recovery still happening */ goto unlock; if (mddev->sync_thread) { /* resync has finished, collect result */ md_unregister_thread(mddev->sync_thread); mddev->sync_thread = NULL; - if (mddev->recovery_running == 0) { + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery)) { /* success...*/ /* activate any spares */ mddev->pers->spare_active(mddev); - mddev->spares = 0; } md_update_sb(mddev); - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); goto unlock; } - if (mddev->recovery_running) { + if (mddev->recovery) { /* that's odd.. */ - mddev->recovery_running = 0; + mddev->recovery = 0; wake_up(&resync_wait); } @@ -3192,7 +3195,6 @@ void md_do_recovery(void *data) * remove any failed drives, then * add spares if possible */ - mddev->spares = 0; ITERATE_RDEV(mddev,rdev,rtmp) { if (rdev->raid_disk >= 0 && rdev->faulty && @@ -3201,35 +3203,35 @@ void md_do_recovery(void *data) rdev->raid_disk = -1; } if (!rdev->faulty && rdev->raid_disk >= 0 && !rdev->in_sync) - mddev->spares++; + spares++; } if (mddev->degraded) { ITERATE_RDEV(mddev,rdev,rtmp) if (rdev->raid_disk < 0 && !rdev->faulty) { - if (mddev->pers->hot_add_disk(mddev,rdev)) { - mddev->spares++; - mddev->recovery_cp = 0; - } + if (mddev->pers->hot_add_disk(mddev,rdev)) + spares++; else break; } } - if (!mddev->spares && (mddev->recovery_cp == MaxSector )) { + if (!spares && (mddev->recovery_cp == MaxSector )) { /* nothing we can do ... */ goto unlock; } if (mddev->pers->sync_request) { + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (!spares) + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, "md_resync"); if (!mddev->sync_thread) { printk(KERN_ERR "md%d: could not start resync thread...\n", mdidx(mddev)); /* leave the spares where they are, it shouldn't hurt */ - mddev->recovery_running = 0; + mddev->recovery = 0; } else { - mddev->recovery_running = 1; md_wakeup_thread(mddev->sync_thread); } } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 152656ef5012..67f3f6d8f875 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -623,10 +623,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) mddev->degraded++; conf->working_disks--; /* - * if recovery was running, stop it now. + * if recovery is running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->in_sync = 0; rdev->faulty = 1; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index abec935d78d1..52a723c4d30e 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -463,10 +463,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev) conf->failed_disks++; rdev->in_sync = 0; /* - * if recovery was running, stop it now. + * if recovery was running, make sure it aborts. */ - if (mddev->recovery_running) - mddev->recovery_running = -EIO; + set_bit(MD_RECOVERY_ERR, &mddev->recovery); } rdev->faulty = 1; printk (KERN_ALERT diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index cf7505f655da..137c64966057 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -210,18 +210,24 @@ struct mddev_s unsigned long curr_resync; /* blocks scheduled */ unsigned long resync_mark; /* a recent timestamp */ unsigned long resync_mark_cnt;/* blocks written at resync_mark */ - /* recovery_running is 0 for no recovery/resync, - * 1 for active recovery - * 2 for active resync - * -error for an error (e.g. -EINTR) - * it can only be set > 0 under reconfig_sem + + /* recovery/resync flags + * RUNNING: a thread is running, or about to be started + * SYNC: actually doing a resync, not a recovery + * ERR: and IO error was detected - abort the resync/recovery + * INTR: someone requested a (clean) early abort. + * DONE: thread is done and is waiting to be reaped */ - int recovery_running; - int recovery_error; /* error from recovery write */ +#define MD_RECOVERY_RUNNING 0 +#define MD_RECOVERY_SYNC 1 +#define MD_RECOVERY_ERR 2 +#define MD_RECOVERY_INTR 3 +#define MD_RECOVERY_DONE 4 + unsigned long recovery; + int in_sync; /* know to not need resync */ struct semaphore reconfig_sem; atomic_t active; - int spares; int degraded; /* whether md should consider * adding a spare -- cgit v1.2.3 From 8eec7ce0744430471c1fce2a503829cd7cd80f11 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:08:54 -0800 Subject: [PATCH] md: Remove md_recoveryd thread for md The md_recoveryd thread is responsible for initiating and cleaning up resync threads. This job can be equally well done by the per-array threads for those arrays which might need it. So the mdrecoveryd thread is gone and the core code that it ran is now run by raid5d, raid1d or multipathd. We add an MD_RECOVERY_NEEDED flag so those daemon don't have to bother trying to lock the md array unless it is likely that something needs to be done. Also modify the names of all threads to have the number of md device. --- drivers/md/md.c | 84 +++++++++++++++++++----------------------- drivers/md/multipath.c | 15 ++++---- drivers/md/raid1.c | 24 ++++++------ drivers/md/raid5.c | 34 ++++++++--------- include/linux/raid/md.h | 7 ++-- include/linux/raid/md_k.h | 7 +++- include/linux/raid/multipath.h | 1 - include/linux/raid/raid1.h | 2 - include/linux/raid/raid5.h | 2 - 9 files changed, 80 insertions(+), 96 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index f1bce4d8bd7d..14fd1eadc8d9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -124,9 +124,6 @@ static ctl_table raid_root_table[] = { { .ctl_name = 0 } }; -static void md_recover_arrays(void); -static mdk_thread_t *md_recovery_thread; - sector_t md_size[MAX_MD_DEVS]; static struct block_device_operations md_fops; @@ -1527,7 +1524,8 @@ static int do_md_run(mddev_t * mddev) mddev->in_sync = 1; md_update_sb(mddev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); set_capacity(disk, md_size[mdidx(mddev)]<<1); return (0); } @@ -1563,7 +1561,8 @@ static int restart_array(mddev_t *mddev) /* * Kick recovery or resync if necessary */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); err = 0; } else { printk(KERN_ERR "md: md%d has no personality assigned.\n", @@ -2133,7 +2132,8 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) * Kick recovery, maybe this spare has to be added to the * array immediately. */ - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); return 0; @@ -2482,7 +2482,7 @@ int md_thread(void * arg) * Detach thread */ - daemonize(thread->name); + daemonize(thread->name, mdidx(thread->mddev)); current->exit_signal = SIGCHLD; allow_signal(SIGKILL); @@ -2503,7 +2503,7 @@ int md_thread(void * arg) complete(thread->event); while (thread->run) { - void (*run)(void *data); + void (*run)(mddev_t *); wait_event_interruptible(thread->wqueue, test_bit(THREAD_WAKEUP, &thread->flags)); @@ -2514,7 +2514,7 @@ int md_thread(void * arg) run = thread->run; if (run) { - run(thread->data); + run(thread->mddev); blk_run_queues(); } if (signal_pending(current)) @@ -2531,8 +2531,8 @@ void md_wakeup_thread(mdk_thread_t *thread) wake_up(&thread->wqueue); } -mdk_thread_t *md_register_thread(void (*run) (void *), - void *data, const char *name) +mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, + const char *name) { mdk_thread_t *thread; int ret; @@ -2549,7 +2549,7 @@ mdk_thread_t *md_register_thread(void (*run) (void *), init_completion(&event); thread->event = &event; thread->run = run; - thread->data = data; + thread->mddev = mddev; thread->name = name; ret = kernel_thread(md_thread, thread, 0); if (ret < 0) { @@ -2584,16 +2584,6 @@ void md_unregister_thread(mdk_thread_t *thread) kfree(thread); } -static void md_recover_arrays(void) -{ - if (!md_recovery_thread) { - MD_BUG(); - return; - } - md_wakeup_thread(md_recovery_thread); -} - - void md_error(mddev_t *mddev, mdk_rdev_t *rdev) { dprintk("md_error dev:(%d:%d), rdev:(%d:%d), (caller: %p,%p,%p,%p).\n", @@ -2611,7 +2601,8 @@ void md_error(mddev_t *mddev, mdk_rdev_t *rdev) if (!mddev->pers->error_handler) return; mddev->pers->error_handler(mddev,rdev); - md_recover_arrays(); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); } /* seq_file implementation /proc/mdstat */ @@ -2897,7 +2888,7 @@ void md_done_sync(mddev_t *mddev, int blocks, int ok) wake_up(&mddev->recovery_wait); if (!ok) { set_bit(MD_RECOVERY_ERR, &mddev->recovery); - md_recover_arrays(); + md_wakeup_thread(mddev->thread); // stop recovery, signal do_sync .... } } @@ -2917,10 +2908,10 @@ void md_write_start(mddev_t *mddev) atomic_inc(&mddev->writes_pending); } -void md_write_end(mddev_t *mddev, mdk_thread_t *thread) +void md_write_end(mddev_t *mddev) { if (atomic_dec_and_test(&mddev->writes_pending) && mddev->safemode) - md_wakeup_thread(thread); + md_wakeup_thread(mddev->thread); } static inline void md_enter_safemode(mddev_t *mddev) { @@ -2950,9 +2941,8 @@ DECLARE_WAIT_QUEUE_HEAD(resync_wait); #define SYNC_MARKS 10 #define SYNC_MARK_STEP (3*HZ) -static void md_do_sync(void *data) +static void md_do_sync(mddev_t *mddev) { - mddev_t *mddev = data; mddev_t *mddev2; unsigned int max_sectors, currspeed = 0, j, window, err; @@ -3129,13 +3119,16 @@ static void md_do_sync(void *data) skip: mddev->curr_resync = 0; set_bit(MD_RECOVERY_DONE, &mddev->recovery); - md_recover_arrays(); + md_wakeup_thread(mddev->thread); } /* - * This is the kernel thread that watches all md arrays for re-sync and other - * action that might be needed. + * This routine is regularly called by all per-raid-array threads to + * deal with generic issues like resync and super-block update. + * Raid personalities that don't have a thread (linear/raid0) do not + * need this as they never do any recovery or update the superblock. + * * It does not do any resync itself, but rather "forks" off other threads * to do that as needed. * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in @@ -3152,19 +3145,24 @@ static void md_do_sync(void *data) * 5/ If array is degraded, try to add spares devices * 6/ If array has spares or is not in-sync, start a resync thread. */ -void md_do_recovery(void *data) +void md_check_recovery(mddev_t *mddev) { - mddev_t *mddev; mdk_rdev_t *rdev; - struct list_head *tmp, *rtmp; + struct list_head *rtmp; dprintk(KERN_INFO "md: recovery thread got woken up ...\n"); - ITERATE_MDDEV(mddev,tmp) if (mddev_lock(mddev)==0) { + if (mddev->ro) + return; + if ( ! ( + mddev->sb_dirty || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || + test_bit(MD_RECOVERY_DONE, &mddev->recovery) + )) + return; + if (mddev_trylock(mddev)==0) { int spares =0; - if (!mddev->raid_disks || !mddev->pers || mddev->ro) - goto unlock; if (mddev->sb_dirty) md_update_sb(mddev); if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && @@ -3226,7 +3224,7 @@ void md_do_recovery(void *data) set_bit(MD_RECOVERY_SYNC, &mddev->recovery); mddev->sync_thread = md_register_thread(md_do_sync, mddev, - "md_resync"); + "md%d_resync"); if (!mddev->sync_thread) { printk(KERN_ERR "md%d: could not start resync thread...\n", mdidx(mddev)); /* leave the spares where they are, it shouldn't hurt */ @@ -3238,8 +3236,6 @@ void md_do_recovery(void *data) unlock: mddev_unlock(mddev); } - dprintk(KERN_INFO "md: recovery thread finished ...\n"); - } int md_notify_reboot(struct notifier_block *this, @@ -3292,7 +3288,6 @@ static void md_geninit(void) int __init md_init(void) { - static char * name = "mdrecoveryd"; int minor; printk(KERN_INFO "md: md driver %d.%d.%d MAX_MD_DEVS=%d, MD_SB_DISKS=%d\n", @@ -3312,11 +3307,6 @@ int __init md_init(void) S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); } - md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); - if (!md_recovery_thread) - printk(KERN_ALERT - "md: bug: couldn't allocate md_recovery_thread\n"); - register_reboot_notifier(&md_notifier); raid_table_header = register_sysctl_table(raid_root_table, 1); @@ -3374,7 +3364,6 @@ static __exit void md_exit(void) { int i; blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS); - md_unregister_thread(md_recovery_thread); for (i=0; i < MAX_MD_DEVS; i++) devfs_remove("md/%d", i); devfs_remove("md"); @@ -3414,4 +3403,5 @@ EXPORT_SYMBOL(md_unregister_thread); EXPORT_SYMBOL(md_wakeup_thread); EXPORT_SYMBOL(md_print_devices); EXPORT_SYMBOL(md_interrupt_thread); +EXPORT_SYMBOL(md_check_recovery); MODULE_LICENSE("GPL"); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 1eaa36356155..bff6174d8782 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -86,7 +86,6 @@ static void multipath_reschedule_retry (struct multipath_bh *mp_bh) { unsigned long flags; mddev_t *mddev = mp_bh->mddev; - multipath_conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); if (multipath_retry_list == NULL) @@ -95,7 +94,7 @@ static void multipath_reschedule_retry (struct multipath_bh *mp_bh) multipath_retry_tail = &mp_bh->next_mp; mp_bh->next_mp = NULL; spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } @@ -333,14 +332,14 @@ abort: * 3. Performs writes following reads for array syncronising. */ -static void multipathd (void *data) +static void multipathd (mddev_t *mddev) { struct multipath_bh *mp_bh; struct bio *bio; unsigned long flags; - mddev_t *mddev; mdk_rdev_t *rdev; + md_check_recovery(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); mp_bh = multipath_retry_list; @@ -470,10 +469,10 @@ static int multipath_run (mddev_t *mddev) } { - const char * name = "multipathd"; + const char * name = "md%d_multipath"; - conf->thread = md_register_thread(multipathd, conf, name); - if (!conf->thread) { + mddev->thread = md_register_thread(multipathd, mddev, name); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -512,7 +511,7 @@ static int multipath_stop (mddev_t *mddev) { multipath_conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); mempool_destroy(conf->pool); kfree(conf); mddev->private = NULL; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 67f3f6d8f875..6d6afd409667 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -225,13 +225,12 @@ static void reschedule_retry(r1bio_t *r1_bio) { unsigned long flags; mddev_t *mddev = r1_bio->mddev; - conf_t *conf = mddev_to_conf(mddev); spin_lock_irqsave(&retry_list_lock, flags); list_add(&r1_bio->retry_list, &retry_list_head); spin_unlock_irqrestore(&retry_list_lock, flags); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); } /* @@ -320,7 +319,7 @@ static int end_request(struct bio *bio, unsigned int bytes_done, int error) * already. */ if (atomic_dec_and_test(&r1_bio->remaining)) { - md_write_end(r1_bio->mddev,conf->thread); + md_write_end(r1_bio->mddev); raid_end_bio_io(r1_bio, uptodate); } } @@ -542,7 +541,7 @@ static int make_request(request_queue_t *q, struct bio * bio) * If all mirrors are non-operational * then return an IO error: */ - md_write_end(mddev,conf->thread); + md_write_end(mddev); raid_end_bio_io(r1_bio, 0); return 0; } @@ -898,17 +897,17 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) * 3. Performs writes following reads for array syncronising. */ -static void raid1d(void *data) +static void raid1d(mddev_t *mddev) { struct list_head *head = &retry_list_head; r1bio_t *r1_bio; struct bio *bio; unsigned long flags; - mddev_t *mddev; - conf_t *conf = data; + conf_t *conf = mddev_to_conf(mddev); mdk_rdev_t *rdev; - md_handle_safemode(conf->mddev); + md_check_recovery(mddev); + md_handle_safemode(mddev); for (;;) { spin_lock_irqsave(&retry_list_lock, flags); @@ -1188,10 +1187,8 @@ static int run(mddev_t *mddev) { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid1d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid1d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid1d, mddev, "md%d_raid1"); + if (!mddev->thread) { printk(THREAD_ERROR, mdidx(mddev)); goto out_free_conf; } @@ -1217,7 +1214,8 @@ static int stop(mddev_t *mddev) { conf_t *conf = mddev_to_conf(mddev); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; if (conf->r1bio_pool) mempool_destroy(conf->r1bio_pool); kfree(conf); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 52a723c4d30e..63d6e4bf843b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -71,12 +71,12 @@ static inline void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) list_add_tail(&sh->lru, &conf->delayed_list); else list_add_tail(&sh->lru, &conf->handle_list); - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } else { if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } list_add_tail(&sh->lru, &conf->inactive_list); atomic_dec(&conf->active_stripes); @@ -912,7 +912,7 @@ static void handle_stripe(struct stripe_head *sh) struct bio *nextbi = bi->bi_next; clear_bit(BIO_UPTODATE, &bi->bi_flags); if (--bi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); bi->bi_next = return_bi; return_bi = bi; } @@ -969,7 +969,7 @@ static void handle_stripe(struct stripe_head *sh) while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) { wbi2 = wbi->bi_next; if (--wbi->bi_phys_segments == 0) { - md_write_end(conf->mddev, conf->thread); + md_write_end(conf->mddev); wbi->bi_next = return_bi; return_bi = wbi; } @@ -1112,7 +1112,7 @@ static void handle_stripe(struct stripe_head *sh) if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) { atomic_dec(&conf->preread_active_stripes); if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) - md_wakeup_thread(conf->thread); + md_wakeup_thread(conf->mddev->thread); } } } @@ -1250,7 +1250,7 @@ static void raid5_unplug_device(void *data) if (blk_remove_plug(q)) raid5_activate_delayed(conf); - md_wakeup_thread(conf->thread); + md_wakeup_thread(mddev->thread); spin_unlock_irqrestore(&conf->device_lock, flags); } @@ -1303,7 +1303,7 @@ static int make_request (request_queue_t *q, struct bio * bi) int bytes = bi->bi_size; if ( bio_data_dir(bi) == WRITE ) - md_write_end(mddev,conf->thread); + md_write_end(mddev); bi->bi_size = 0; bi->bi_end_io(bi, bytes, 0); } @@ -1355,16 +1355,17 @@ static int sync_request (mddev_t *mddev, sector_t sector_nr, int go_faster) * During the scan, completed stripes are saved for us by the interrupt * handler, so that they will not have to wait for our next wakeup. */ -static void raid5d (void *data) +static void raid5d (mddev_t *mddev) { struct stripe_head *sh; - raid5_conf_t *conf = data; - mddev_t *mddev = conf->mddev; + raid5_conf_t *conf = mddev_to_conf(mddev); int handled; PRINTK("+++ raid5d active\n"); + md_check_recovery(mddev); md_handle_safemode(mddev); + handled = 0; spin_lock_irq(&conf->device_lock); while (1) { @@ -1485,10 +1486,8 @@ static int run (mddev_t *mddev) } { - snprintf(conf->thread_name,MD_THREAD_NAME_MAX,"raid5d_md%d",mdidx(mddev)); - - conf->thread = md_register_thread(raid5d, conf, conf->thread_name); - if (!conf->thread) { + mddev->thread = md_register_thread(raid5d, mddev, "md%d_raid5"); + if (!mddev->thread) { printk(KERN_ERR "raid5: couldn't allocate thread for md%d\n", mdidx(mddev)); goto abort; } @@ -1499,7 +1498,7 @@ static int run (mddev_t *mddev) if (grow_stripes(conf, conf->max_nr_stripes)) { printk(KERN_ERR "raid5: couldn't allocate %dkB for buffers\n", memory); shrink_stripes(conf); - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); goto abort; } else printk(KERN_INFO "raid5: allocated %dkB for md%d\n", memory, mdidx(mddev)); @@ -1535,7 +1534,8 @@ static int stop (mddev_t *mddev) { raid5_conf_t *conf = (raid5_conf_t *) mddev->private; - md_unregister_thread(conf->thread); + md_unregister_thread(mddev->thread); + mddev->thread = NULL; shrink_stripes(conf); free_pages((unsigned long) conf->stripe_hashtbl, HASH_PAGES_ORDER); kfree(conf); @@ -1573,8 +1573,6 @@ static void printall (raid5_conf_t *conf) } } spin_unlock_irq(&conf->device_lock); - - PRINTK("--- raid5d inactive\n"); } #endif diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index 3260343d634e..3d4faa9f7171 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -69,13 +69,14 @@ extern inline char * bdev_partition_name (struct block_device *bdev) } extern int register_md_personality (int p_num, mdk_personality_t *p); extern int unregister_md_personality (int p_num); -extern mdk_thread_t * md_register_thread (void (*run) (void *data), - void *data, const char *name); +extern mdk_thread_t * md_register_thread (void (*run) (mddev_t *mddev), + mddev_t *mddev, const char *name); extern void md_unregister_thread (mdk_thread_t *thread); extern void md_wakeup_thread(mdk_thread_t *thread); +extern void md_check_recovery(mddev_t *mddev); extern void md_interrupt_thread (mdk_thread_t *thread); extern void md_write_start(mddev_t *mddev); -extern void md_write_end(mddev_t *mddev, mdk_thread_t *thread); +extern void md_write_end(mddev_t *mddev); extern void md_handle_safemode(mddev_t *mddev); extern void md_done_sync(mddev_t *mddev, int blocks, int ok); extern void md_sync_acct(mdk_rdev_t *rdev, unsigned long nr_sectors); diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 137c64966057..eb232ae1c768 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -206,12 +206,14 @@ struct mddev_s char uuid[16]; + struct mdk_thread_s *thread; /* management thread */ struct mdk_thread_s *sync_thread; /* doing resync or reconstruct */ unsigned long curr_resync; /* blocks scheduled */ unsigned long resync_mark; /* a recent timestamp */ unsigned long resync_mark_cnt;/* blocks written at resync_mark */ /* recovery/resync flags + * NEEDED: we might need to start a resync/recover * RUNNING: a thread is running, or about to be started * SYNC: actually doing a resync, not a recovery * ERR: and IO error was detected - abort the resync/recovery @@ -223,6 +225,7 @@ struct mddev_s #define MD_RECOVERY_ERR 2 #define MD_RECOVERY_INTR 3 #define MD_RECOVERY_DONE 4 +#define MD_RECOVERY_NEEDED 5 unsigned long recovery; int in_sync; /* know to not need resync */ @@ -298,8 +301,8 @@ extern mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr); ITERATE_RDEV_GENERIC(pending_raid_disks,rdev,tmp) typedef struct mdk_thread_s { - void (*run) (void *data); - void *data; + void (*run) (mddev_t *mddev); + mddev_t *mddev; wait_queue_head_t wqueue; unsigned long flags; struct completion *event; diff --git a/include/linux/raid/multipath.h b/include/linux/raid/multipath.h index 50db7f3c8c57..42d040ea63df 100644 --- a/include/linux/raid/multipath.h +++ b/include/linux/raid/multipath.h @@ -13,7 +13,6 @@ struct multipath_private_data { struct multipath_info multipaths[MD_SB_DISKS]; int raid_disks; int working_disks; - mdk_thread_t *thread; spinlock_t device_lock; mempool_t *pool; diff --git a/include/linux/raid/raid1.h b/include/linux/raid/raid1.h index 9ed30f4d0748..cc7aa899a613 100644 --- a/include/linux/raid/raid1.h +++ b/include/linux/raid/raid1.h @@ -19,7 +19,6 @@ struct r1_private_data_s { int working_disks; int last_used; sector_t next_seq_sect; - mdk_thread_t *thread; spinlock_t device_lock; /* for use when syncing mirrors: */ @@ -34,7 +33,6 @@ struct r1_private_data_s { mempool_t *r1bio_pool; mempool_t *r1buf_pool; - char thread_name[MD_THREAD_NAME_MAX]; }; typedef struct r1_private_data_s conf_t; diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 636e10b84502..c9eea7c884d0 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -203,7 +203,6 @@ struct disk_info { struct raid5_private_data { struct stripe_head **stripe_hashtbl; mddev_t *mddev; - mdk_thread_t *thread; struct disk_info disks[MD_SB_DISKS]; struct disk_info *spare; int chunk_size, level, algorithm; @@ -226,7 +225,6 @@ struct raid5_private_data { * waiting for 25% to be free */ spinlock_t device_lock; - char thread_name[MD_THREAD_NAME_MAX]; }; typedef struct raid5_private_data raid5_conf_t; -- cgit v1.2.3 From 9578658e998baaf5bf36d1053b690bd28976b1ca Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:09:01 -0800 Subject: [PATCH] md: Fulltime delayed 'safe_mode' for md From: Angus Sawyer If there are no writes for 20 milliseconds, write out superblock to mark array as clean. Write out superblock with dirty flag before allowing any further write to succeed. If an md thread gets signaled with SIGKILL, reduce the delay to 0. Also tidy up some printk's and make sure writing the superblock isn't noisy. --- drivers/md/md.c | 64 ++++++++++++++++++++++++++++++----------------- include/linux/raid/md_k.h | 4 ++- 2 files changed, 44 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 14fd1eadc8d9..5689df88774a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -219,6 +219,7 @@ static mddev_t * mddev_find(int unit) init_MUTEX(&new->reconfig_sem); INIT_LIST_HEAD(&new->disks); INIT_LIST_HEAD(&new->all_mddevs); + init_timer(&new->safemode_timer); atomic_set(&new->active, 1); blk_queue_make_request(&new->queue, md_fail_request); @@ -701,10 +702,8 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->recovery_cp = mddev->recovery_cp; sb->cp_events_hi = (mddev->events>>32); sb->cp_events_lo = (u32)mddev->events; - if (mddev->recovery_cp == MaxSector) { - printk(KERN_INFO "md: marking sb clean...\n"); + if (mddev->recovery_cp == MaxSector) sb->state = (1<< MD_SB_CLEAN); - } } else sb->recovery_cp = 0; @@ -1005,7 +1004,7 @@ static int write_disk_sb(mdk_rdev_t * rdev) sb_offset = calc_dev_sboffset(rdev->bdev); if (rdev->sb_offset != sb_offset) { - printk(KERN_INFO "%s's sb offset has changed from %llu to %llu, skipping\n", + printk("%s's sb offset has changed from %llu to %llu, skipping\n", bdev_partition_name(rdev->bdev), (unsigned long long)rdev->sb_offset, (unsigned long long)sb_offset); @@ -1025,7 +1024,7 @@ static int write_disk_sb(mdk_rdev_t * rdev) goto skip; } - printk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), (unsigned long long)sb_offset); + dprintk("(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), (unsigned long long)sb_offset); if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) goto fail; @@ -1076,20 +1075,20 @@ repeat: if (!mddev->persistent) return; - printk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", + dprintk(KERN_INFO "md: updating md%d RAID superblock on device (in sync %d)\n", mdidx(mddev),mddev->in_sync); err = 0; ITERATE_RDEV(mddev,rdev,tmp) { - printk(KERN_INFO "md: "); + dprintk(KERN_INFO "md: "); if (rdev->faulty) - printk("(skipping faulty "); + dprintk("(skipping faulty "); - printk("%s ", bdev_partition_name(rdev->bdev)); + dprintk("%s ", bdev_partition_name(rdev->bdev)); if (!rdev->faulty) { err += write_disk_sb(rdev); } else - printk(")\n"); + dprintk(")\n"); if (!err && mddev->level == LEVEL_MULTIPATH) /* only need to write one superblock... */ break; @@ -1377,6 +1376,16 @@ static struct gendisk *md_probe(dev_t dev, int *part, void *data) return NULL; } +void md_wakeup_thread(mdk_thread_t *thread); + +static void md_safemode_timeout(unsigned long data) +{ + mddev_t *mddev = (mddev_t *) data; + + mddev->safemode = 1; + md_wakeup_thread(mddev->thread); +} + #define TOO_BIG_CHUNKSIZE KERN_ERR \ "too big chunk_size: %d > %d\n" @@ -1518,10 +1527,10 @@ static int do_md_run(mddev_t * mddev) } atomic_set(&mddev->writes_pending,0); mddev->safemode = 0; - if (mddev->pers->sync_request) - mddev->in_sync = 0; - else - mddev->in_sync = 1; + mddev->safemode_timer.function = md_safemode_timeout; + mddev->safemode_timer.data = (unsigned long) mddev; + mddev->safemode_delay = (20 * HZ)/1000 +1; /* 20 msec delay */ + mddev->in_sync = 1; md_update_sb(mddev); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); @@ -1551,7 +1560,6 @@ static int restart_array(mddev_t *mddev) goto out; mddev->safemode = 0; - mddev->in_sync = 0; md_update_sb(mddev); mddev->ro = 0; set_disk_ro(disk, 0); @@ -1597,6 +1605,8 @@ static int do_md_stop(mddev_t * mddev, int ro) mddev->sync_thread = NULL; } + del_timer_sync(&mddev->safemode_timer); + invalidate_device(mk_kdev(disk->major, disk->first_minor), 1); if (ro) { @@ -1886,7 +1896,7 @@ static int get_array_info(mddev_t * mddev, void * arg) info.utime = mddev->utime; info.state = 0; - if (mddev->recovery_cp == MaxSector) + if (mddev->in_sync) info.state = (1<safemode && !atomic_read(&mddev->writes_pending)) { + if (!atomic_read(&mddev->writes_pending)) { mddev_lock_uninterruptible(mddev); - atomic_inc(&mddev->writes_pending); if (mddev->in_sync) { mddev->in_sync = 0; + del_timer(&mddev->safemode_timer); md_update_sb(mddev); } + atomic_inc(&mddev->writes_pending); mddev_unlock(mddev); } else atomic_inc(&mddev->writes_pending); @@ -2910,12 +2921,16 @@ void md_write_start(mddev_t *mddev) void md_write_end(mddev_t *mddev) { - if (atomic_dec_and_test(&mddev->writes_pending) && mddev->safemode) - md_wakeup_thread(mddev->thread); + if (atomic_dec_and_test(&mddev->writes_pending)) { + if (mddev->safemode == 2) + md_wakeup_thread(mddev->thread); + else + mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay); + } } + static inline void md_enter_safemode(mddev_t *mddev) { - mddev_lock_uninterruptible(mddev); if (mddev->safemode && !atomic_read(&mddev->writes_pending) && !mddev->in_sync && mddev->recovery_cp == MaxSector) { @@ -2923,13 +2938,16 @@ static inline void md_enter_safemode(mddev_t *mddev) md_update_sb(mddev); } mddev_unlock(mddev); + + if (mddev->safemode == 1) + mddev->safemode = 0; } void md_handle_safemode(mddev_t *mddev) { if (signal_pending(current)) { - printk(KERN_INFO "md: md%d in safe mode\n",mdidx(mddev)); - mddev->safemode= 1; + printk(KERN_INFO "md: md%d in immediate safe mode\n",mdidx(mddev)); + mddev->safemode = 2; flush_signals(current); } if (mddev->safemode) diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index eb232ae1c768..9f071d407f7b 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -239,9 +239,11 @@ struct mddev_s atomic_t recovery_active; /* blocks scheduled, but not written */ wait_queue_head_t recovery_wait; sector_t recovery_cp; - int safemode; /* if set, update "clean" superblock + unsigned int safemode; /* if set, update "clean" superblock * when no writes pending. */ + unsigned int safemode_delay; + struct timer_list safemode_timer; atomic_t writes_pending; request_queue_t queue; /* for plugging ... */ -- cgit v1.2.3 From 264b2f7c01ecfa3c9dbb376c9cf5a3aeb28dba6b Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:09:06 -0800 Subject: [PATCH] md: Fix bad interaction between sync checkpointing and recovery Md devices (raid1/raid5) can resync or recover. There are similar but importantly different. resync happens after an unclean shutdown recovery happens when a failed drive is being replaced by a hot spare. The sync-checkpoint code confused the two somewhat and this causes problems. This patch makes sure "recovery_cp" only relates to resync, not recovery. It also fixes a small problem with recording spares in the superblock. --- drivers/md/md.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index 5689df88774a..8a8dc92a527d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -713,7 +713,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->disks[0].state = (1<raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev2->in_sync && !rdev2->faulty) rdev2->desc_nr = rdev2->raid_disk; else rdev2->desc_nr = next_spare++; @@ -722,7 +722,7 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) d->number = rdev2->desc_nr; d->major = MAJOR(rdev2->bdev->bd_dev); d->minor = MINOR(rdev2->bdev->bd_dev); - if (rdev2->raid_disk >= 0) + if (rdev2->raid_disk >= 0 && rdev->in_sync && !rdev2->faulty) d->raid_disk = rdev2->raid_disk; else d->raid_disk = rdev2->desc_nr; /* compatibility */ @@ -3020,9 +3020,13 @@ static void md_do_sync(mddev_t *mddev) sysctl_speed_limit_max); is_mddev_idle(mddev); /* this also initializes IO event counters */ + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + j = mddev->recovery_cp; + else + j = 0; for (m = 0; m < SYNC_MARKS; m++) { mark[m] = jiffies; - mark_cnt[m] = mddev->recovery_cp; + mark_cnt[m] = j; } last_mark = 0; mddev->resync_mark = mark[last_mark]; @@ -3039,10 +3043,10 @@ static void md_do_sync(mddev_t *mddev) init_waitqueue_head(&mddev->recovery_wait); last_check = 0; - if (mddev->recovery_cp) + if (j) printk(KERN_INFO "md: resuming recovery of md%d from checkpoint.\n", mdidx(mddev)); - for (j = mddev->recovery_cp; j < max_sectors;) { + while (j < max_sectors) { int sectors; sectors = mddev->pers->sync_request(mddev, j, currspeed < sysctl_speed_limit_min); -- cgit v1.2.3 From b931e4595b0c1ba56decec4c85354b772675c58e Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:09:13 -0800 Subject: [PATCH] md: Allow components of MD raid array to have data start at offset from start of device. Normally the data stored on a component of a RAID array is stored from the start of the device. This patch allows a per-device data_offset so the data can start elsewhere. This will allow RAID arrays where the metadata is at the head of the device rather than the tail. --- drivers/md/linear.c | 2 +- drivers/md/md.c | 2 ++ drivers/md/raid0.c | 2 +- drivers/md/raid1.c | 10 +++++----- drivers/md/raid5.c | 2 +- include/linux/raid/md_k.h | 1 + 6 files changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 380b28ce026b..00e9abe0f015 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -203,7 +203,7 @@ static int linear_make_request (request_queue_t *q, struct bio *bio) return 0; } bio->bi_bdev = tmp_dev->rdev->bdev; - bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1); + bio->bi_sector = bio->bi_sector - (tmp_dev->offset << 1) + tmp_dev->rdev->data_offset; return 1; } diff --git a/drivers/md/md.c b/drivers/md/md.c index 8a8dc92a527d..28c3a08eea76 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -555,6 +555,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) } rdev->preferred_minor = sb->md_minor; + rdev->data_offset = 0; if (refdev == 0) ret = 1; @@ -1137,6 +1138,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) rdev->desc_nr = -1; rdev->faulty = 0; rdev->in_sync = 0; + rdev->data_offset = 0; atomic_set(&rdev->nr_pending, 0); size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 00a79d9b364b..99de04b380a3 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -349,7 +349,7 @@ static int raid0_make_request (request_queue_t *q, struct bio *bio) * is the only IO operation happening on this bh. */ bio->bi_bdev = tmp_dev->bdev; - bio->bi_sector = rsect; + bio->bi_sector = rsect + tmp_dev->data_offset; /* * Let the main block layer submit the IO and resolve recursion: diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 6d6afd409667..0dd391e3d3b1 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -493,7 +493,7 @@ static int make_request(request_queue_t *q, struct bio * bio) BUG(); r1_bio->read_bio = read_bio; - read_bio->bi_sector = r1_bio->sector; + read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_request; read_bio->bi_rw = r1_bio->cmd; @@ -528,7 +528,7 @@ static int make_request(request_queue_t *q, struct bio * bio) mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = end_request; mbio->bi_rw = r1_bio->cmd; @@ -856,7 +856,7 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio) mbio = bio_clone(bio, GFP_NOIO); r1_bio->write_bios[i] = mbio; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; - mbio->bi_sector = r1_bio->sector; + mbio->bi_sector = r1_bio->sector | conf->mirrors[i].rdev->data_offset; mbio->bi_end_io = end_sync_write; mbio->bi_rw = WRITE; mbio->bi_private = r1_bio; @@ -934,7 +934,7 @@ static void raid1d(mddev_t *mddev) printk(REDIRECT_SECTOR, bdev_partition_name(rdev->bdev), (unsigned long long)r1_bio->sector); bio->bi_bdev = rdev->bdev; - bio->bi_sector = r1_bio->sector; + bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_rw = r1_bio->cmd; generic_make_request(bio); @@ -1045,7 +1045,7 @@ static int sync_request(mddev_t *mddev, sector_t sector_nr, int go_faster) read_bio = bio_clone(r1_bio->master_bio, GFP_NOIO); - read_bio->bi_sector = sector_nr; + read_bio->bi_sector = sector_nr + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = end_sync_read; read_bio->bi_rw = READ; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 63d6e4bf843b..51a8e2d721d6 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1206,7 +1206,7 @@ static void handle_stripe(struct stripe_head *sh) bi->bi_bdev = rdev->bdev; PRINTK("for %llu schedule op %ld on disc %d\n", (unsigned long long)sh->sector, bi->bi_rw, i); atomic_inc(&sh->count); - bi->bi_sector = sh->sector; + bi->bi_sector = sh->sector + rdev->data_offset; bi->bi_flags = 1 << BIO_UPTODATE; bi->bi_vcnt = 1; bi->bi_idx = 0; diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index 9f071d407f7b..02bdf9db2ae2 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -155,6 +155,7 @@ struct mdk_rdev_s struct page *sb_page; int sb_loaded; + sector_t data_offset; /* start of data in array */ sector_t sb_offset; int preferred_minor; /* autorun support */ -- cgit v1.2.3 From a3a173cd2d5db3a807a63e79a6f3ec8bce058129 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:09:20 -0800 Subject: [PATCH] md: Allow md to select between superblock formats The code to understand a specific superblock format is already highly localised in md. This patch defines a user-space interface for selecting which superblock format to use, and obeys that selection. Md currently has a concept of 3 version numbers: A major version number A minor version number A patch version number There historically seems to be some confusion about whether these refer to a version of the superblock layout, or a version of the software. We will now define that: the "major_version" defines the superblock handler. '0' is the current superblock format. All new formats will need new numbers. the "minor_version" can specify minor variations in the superblock, such as different location on the device the "patch_version" will be used to indicate new extenstions to the software.. patch_version=1 will mean multiple superblock support. A superblock version number is selected by specifing major_version in SET_ARRAY_INFO ioctl. This patch: Updates Documentation/md.txt with details of new interface. Generalises desc_nr handling and makes sure that an array never has two devices with the same desc_nr. makes sure mddev->major_version is always valid and is 0 by default. uses mddev->major_version to select superblock handlers. Modifies set_array_info to just record version number if raid_disks==0 Makes sure max_disks is always set correctly. Determines device size when reading superblock, or a hot-add/add-new. --- Documentation/md.txt | 68 +++++++++++- drivers/md/md.c | 287 ++++++++++++++++++++++++++++++++------------------- 2 files changed, 243 insertions(+), 112 deletions(-) (limited to 'drivers') diff --git a/Documentation/md.txt b/Documentation/md.txt index cecc9beba2fb..203231307a26 100644 --- a/Documentation/md.txt +++ b/Documentation/md.txt @@ -1,10 +1,10 @@ Tools that manage md devices can be found at - http://www..kernel.org/pub/linux/daemons/raid/.... + http://www..kernel.org/pub/linux/utils/raid/.... -You can boot (if you selected boot support in the configuration) with your md -device with the following kernel command lines: +You can boot with your md device with the following kernel command +lines: for old raid arrays without persistent superblocks: md=,,,,dev0,dev1,...,devn @@ -33,4 +33,64 @@ dev0-devn: e.g. /dev/hda1,/dev/hdc1,/dev/sda1,/dev/sdb1 A possible loadlin line (Harald Hoyer ) looks like this: e:\loadlin\loadlin e:\zimage root=/dev/md0 md=0,0,4,0,/dev/hdb2,/dev/hdc3 ro - + +------------------------------- +The md driver can support a variety of different superblock formats. +(It doesn't yet, but it can) + +The kernel does *NOT* autodetect which format superblock is being +used. It must be told. + +Superblock format '0' is treated differently to others for legacy +reasons. + + +General Rules - apply for all superblock formats +------------------------------------------------ + +An array is 'created' by writing appropriate superblocks to all +devices. +It is 'assembled' by associating each of these devices with an +particular md virtual device. Once it is completely assembled, it can +be accessed. + +An array should be created by a user-space tool. This will write +superblocks to all devices. It will usually mark the array as +'unclean', or with some devices missing so that the kernel md driver +can create approrpriate redundancy (copying in raid1, parity +calculation in raid4/5). + +When an array is assembled, it is first initialised with the +SET_ARRAY_INFO ioctl. This contains, in particular, a major and minor +version number. The major version number selects which superblock +format is to be used. The minor number might be used to tune handling +of the format, such as suggesting where on each device to look for the +superblock. + +Then each device is added using the ADD_NEW_DISK ioctl. This +provides, in particular, a major and minor number identifying the +device to add. + +The array is started with the RUN_ARRAY ioctl. + +Once started, new devices can be added. They should have an +appropriate superblock written to them, and then passed be in with +ADD_NEW_DISK. + +Devices that have failed or are not yet active can be detached from an +array using HOT_REMOVE_DISK. + + +Specific Rules that apply to format-0 super block arrays, and + arrays with no superblock (non-presistant). +------------------------------------------------------------- + +An array can be 'created' by describing the array (level, chunksize +etc) in a SET_ARRAY_INFO ioctl. This must has major_version==0 and +raid_disks != 0. +Then uninitialised devices can be added with ADD_NEW_DISK. The +structure passed to ADD_NEW_DISK must specify the state of the device +and it's role in the array. + +One started with RUN_ARRAY, uninitialised spares can be added with +HOT_ADD_DISK. diff --git a/drivers/md/md.c b/drivers/md/md.c index 28c3a08eea76..ca726530cd81 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -270,40 +270,35 @@ static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev) return NULL; } -static sector_t calc_dev_sboffset(struct block_device *bdev) +inline static sector_t calc_dev_sboffset(struct block_device *bdev) { sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; return MD_NEW_SIZE_BLOCKS(size); } -static sector_t calc_dev_size(struct block_device *bdev, mddev_t *mddev) +static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size) { sector_t size; - if (mddev->persistent) - size = calc_dev_sboffset(bdev); - else - size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; - if (mddev->chunk_size) - size &= ~((sector_t)mddev->chunk_size/1024 - 1); + size = rdev->sb_offset; + + if (chunk_size) + size &= ~((sector_t)chunk_size/1024 - 1); return size; } static sector_t zoned_raid_size(mddev_t *mddev) { - sector_t mask; mdk_rdev_t * rdev; struct list_head *tmp; /* * do size and offset calculations. */ - mask = ~((sector_t)mddev->chunk_size/1024 - 1); - ITERATE_RDEV(mddev,rdev,tmp) { - rdev->size &= mask; + ITERATE_RDEV(mddev,rdev,tmp) md_size[mdidx(mddev)] += rdev->size; - } + return 0; } @@ -387,7 +382,6 @@ static int sync_page_io(struct block_device *bdev, sector_t sector, int size, static int read_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; if (!rdev->sb_page) { MD_BUG(); @@ -396,16 +390,8 @@ static int read_disk_sb(mdk_rdev_t * rdev) if (rdev->sb_loaded) return 0; - /* - * Calculate the position of the superblock, - * it's at the end of the disk. - * - * It also happens to be a multiple of 4Kb. - */ - sb_offset = calc_dev_sboffset(rdev->bdev); - rdev->sb_offset = sb_offset; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) + if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, READ)) goto fail; rdev->sb_loaded = 1; return 0; @@ -484,7 +470,7 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) * We rely on user-space to write the initial superblock, and support * reading and updating of superblocks. * Interface methods are: - * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev) + * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version) * loads and validates a superblock on dev. * if refdev != NULL, compare superblocks on both devices * Return: @@ -509,7 +495,7 @@ static unsigned int calc_sb_csum(mdp_super_t * sb) struct super_type { char *name; struct module *owner; - int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev); + int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version); int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); }; @@ -517,10 +503,20 @@ struct super_type { /* * load_super for 0.90.0 */ -static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) +static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) { mdp_super_t *sb; int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock, + * it's at the end of the disk. + * + * It also happens to be a multiple of 4Kb. + */ + sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->sb_offset = sb_offset; ret = read_disk_sb(rdev); if (ret) return ret; @@ -557,6 +553,11 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) rdev->preferred_minor = sb->md_minor; rdev->data_offset = 0; + if (sb->level == MULTIPATH) + rdev->desc_nr = -1; + else + rdev->desc_nr = sb->this_disk.number; + if (refdev == 0) ret = 1; else { @@ -581,7 +582,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev) else ret = 0; } - + rdev->size = calc_dev_size(rdev, sb->chunk_size); abort: return ret; @@ -596,7 +597,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page); if (mddev->raid_disks == 0) { - mddev->major_version = sb->major_version; + mddev->major_version = 0; mddev->minor_version = sb->minor_version; mddev->patch_version = sb->patch_version; mddev->persistent = ! sb->not_persistent; @@ -633,7 +634,6 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) return -EINVAL; } if (mddev->level != LEVEL_MULTIPATH) { - rdev->desc_nr = sb->this_disk.number; rdev->raid_disk = -1; rdev->in_sync = rdev->faulty = 0; desc = sb->disks + rdev->desc_nr; @@ -801,13 +801,13 @@ static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) static LIST_HEAD(pending_raid_disks); -static void bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) +static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { mdk_rdev_t *same_pdev; if (rdev->mddev) { MD_BUG(); - return; + return -EINVAL; } same_pdev = match_dev_unit(mddev, rdev); if (same_pdev) @@ -817,9 +817,25 @@ static void bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) mdidx(mddev), bdev_partition_name(rdev->bdev), bdev_partition_name(same_pdev->bdev)); + /* Verify rdev->desc_nr is unique. + * If it is -1, assign a free number, else + * check number is not in use + */ + if (rdev->desc_nr < 0) { + int choice = 0; + if (mddev->pers) choice = mddev->raid_disks; + while (find_rdev_nr(mddev, choice)) + choice++; + rdev->desc_nr = choice; + } else { + if (find_rdev_nr(mddev, rdev->desc_nr)) + return -EBUSY; + } + list_add(&rdev->same_set, &mddev->disks); rdev->mddev = mddev; printk(KERN_INFO "md: bind<%s>\n", bdev_partition_name(rdev->bdev)); + return 0; } static void unbind_rdev_from_array(mdk_rdev_t * rdev) @@ -907,6 +923,7 @@ static void export_array(mddev_t *mddev) if (!list_empty(&mddev->disks)) MD_BUG(); mddev->raid_disks = 0; + mddev->major_version = 0; } #undef BAD_CSUM @@ -991,8 +1008,6 @@ void md_print_devices(void) static int write_disk_sb(mdk_rdev_t * rdev) { - sector_t sb_offset; - sector_t size; if (!rdev->sb_loaded) { MD_BUG(); @@ -1003,35 +1018,12 @@ static int write_disk_sb(mdk_rdev_t * rdev) return 1; } - sb_offset = calc_dev_sboffset(rdev->bdev); - if (rdev->sb_offset != sb_offset) { - printk("%s's sb offset has changed from %llu to %llu, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->sb_offset, - (unsigned long long)sb_offset); - goto skip; - } - /* - * If the disk went offline meanwhile and it's just a spare, then - * its size has changed to zero silently, and the MD code does - * not yet know that it's faulty. - */ - size = calc_dev_size(rdev->bdev, rdev->mddev); - if (size != rdev->size) { - printk(KERN_INFO "%s's size has changed from %llu to %llu since import, skipping\n", - bdev_partition_name(rdev->bdev), - (unsigned long long)rdev->size, - (unsigned long long)size); - goto skip; - } - - dprintk("(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), (unsigned long long)sb_offset); + dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", bdev_partition_name(rdev->bdev), + (unsigned long long)rdev->sb_offset); + + if (sync_page_io(rdev->bdev, rdev->sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) + return 0; - if (!sync_page_io(rdev->bdev, sb_offset<<1, MD_SB_BYTES, rdev->sb_page, WRITE)) - goto fail; -skip: - return 0; -fail: printk("md: write_disk_sb failed for device %s\n", bdev_partition_name(rdev->bdev)); return 1; } @@ -1042,7 +1034,8 @@ static void sync_sbs(mddev_t * mddev) struct list_head *tmp; ITERATE_RDEV(mddev,rdev,tmp) { - super_90_sync(mddev, rdev); + super_types[mddev->major_version]. + sync_super(mddev, rdev); rdev->sb_loaded = 1; } } @@ -1104,7 +1097,7 @@ repeat: } /* - * Import a device. If 'on_disk', then sanity check the superblock + * Import a device. If 'super_format' >= 0, then sanity check the superblock * * mark the device faulty if: * @@ -1113,7 +1106,7 @@ repeat: * * a faulty rdev _never_ has rdev->sb set. */ -static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) +static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor) { int err; mdk_rdev_t *rdev; @@ -1150,8 +1143,9 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int on_disk) goto abort_free; } - if (on_disk) { - err = super_90_load(rdev, NULL); + if (super_format >= 0) { + err = super_types[super_format]. + load_super(rdev, NULL, super_minor); if (err == -EINVAL) { printk(KERN_WARNING "md: %s has invalid sb, not importing!\n", bdev_partition_name(rdev->bdev)); @@ -1204,7 +1198,8 @@ static int analyze_sbs(mddev_t * mddev) freshest = NULL; ITERATE_RDEV(mddev,rdev,tmp) - switch (super_90_load(rdev, freshest)) { + switch (super_types[mddev->major_version]. + load_super(rdev, freshest, mddev->minor_version)) { case 1: freshest = rdev; break; @@ -1216,12 +1211,14 @@ static int analyze_sbs(mddev_t * mddev) } - super_90_validate(mddev, freshest); + super_types[mddev->major_version]. + validate_super(mddev, freshest); i = 0; ITERATE_RDEV(mddev,rdev,tmp) { if (rdev != freshest) - if (super_90_validate(mddev, rdev)) { + if (super_types[mddev->major_version]. + validate_super(mddev, rdev)) { printk(KERN_WARNING "md: kicking non-fresh %s from array!\n", bdev_partition_name(rdev->bdev)); kick_rdev_from_array(rdev); @@ -1276,11 +1273,6 @@ static int device_size_calculation(mddev_t * mddev) ITERATE_RDEV(mddev,rdev,tmp) { if (rdev->faulty) continue; - if (rdev->size) { - MD_BUG(); - continue; - } - rdev->size = calc_dev_size(rdev->bdev, mddev); if (rdev->size < mddev->chunk_size / 1024) { printk(KERN_WARNING "md: Dev %s smaller than chunk_size: %lluk < %dk\n", @@ -1709,7 +1701,7 @@ static void autorun_devices(void) printk(KERN_INFO "md: considering %s ...\n", bdev_partition_name(rdev0->bdev)); INIT_LIST_HEAD(&candidates); ITERATE_RDEV_PENDING(rdev,tmp) - if (super_90_load(rdev, rdev0) >= 0) { + if (super_90_load(rdev, rdev0, 0) >= 0) { printk(KERN_INFO "md: adding %s ...\n", bdev_partition_name(rdev->bdev)); list_move(&rdev->same_set, &candidates); } @@ -1727,7 +1719,8 @@ static void autorun_devices(void) if (mddev_lock(mddev)) printk(KERN_WARNING "md: md%d locked, cannot run\n", mdidx(mddev)); - else if (mddev->raid_disks || !list_empty(&mddev->disks)) { + else if (mddev->raid_disks || mddev->major_version + || !list_empty(&mddev->disks)) { printk(KERN_WARNING "md: md%d already running, cannot run %s\n", mdidx(mddev), bdev_partition_name(rdev0->bdev)); mddev_unlock(mddev); @@ -1735,7 +1728,8 @@ static void autorun_devices(void) printk(KERN_INFO "md: created md%d\n", mdidx(mddev)); ITERATE_RDEV_GENERIC(candidates,rdev,tmp) { list_del_init(&rdev->same_set); - bind_rdev_to_array(rdev, mddev); + if (bind_rdev_to_array(rdev, mddev)) + export_rdev(rdev); } autorun_array(mddev); mddev_unlock(mddev); @@ -1788,7 +1782,7 @@ static int autostart_array(dev_t startdev) mdp_super_t *sb = NULL; mdk_rdev_t *start_rdev = NULL, *rdev; - start_rdev = md_import_device(startdev, 1); + start_rdev = md_import_device(startdev, 0, 0); if (IS_ERR(start_rdev)) { printk(KERN_WARNING "md: could not import %s!\n", partition_name(startdev)); return err; @@ -1822,7 +1816,7 @@ static int autostart_array(dev_t startdev) continue; if (dev == startdev) continue; - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, 0, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: could not import %s, trying to run array nevertheless.\n", partition_name(dev)); @@ -1884,10 +1878,9 @@ static int get_array_info(mddev_t * mddev, void * arg) } } - info.major_version = mddev->major_version; info.major_version = mddev->major_version; info.minor_version = mddev->minor_version; - info.patch_version = mddev->patch_version; + info.patch_version = 1; info.ctime = mddev->ctime; info.level = mddev->level; info.size = mddev->size; @@ -1953,13 +1946,13 @@ static int get_disk_info(mddev_t * mddev, void * arg) static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) { - sector_t size; mdk_rdev_t *rdev; dev_t dev; dev = MKDEV(info->major,info->minor); if (!mddev->raid_disks) { + int err; /* expecting a device which has a superblock */ - rdev = md_import_device(dev, 1); + rdev = md_import_device(dev, mddev->major_version, mddev->minor_version); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); return PTR_ERR(rdev); @@ -1967,7 +1960,8 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) if (!list_empty(&mddev->disks)) { mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, mdk_rdev_t, same_set); - int err = super_90_load(rdev, rdev0); + int err = super_types[mddev->major_version] + .load_super(rdev, rdev0, mddev->minor_version); if (err < 0) { printk(KERN_WARNING "md: %s has different UUID to %s\n", bdev_partition_name(rdev->bdev), bdev_partition_name(rdev0->bdev)); @@ -1975,12 +1969,52 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info) return -EINVAL; } } - bind_rdev_to_array(rdev, mddev); - return 0; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + return err; + } + + /* + * add_new_disk can be used once the array is assembled + * to add "hot spares". They must already have a superblock + * written + */ + if (mddev->pers) { + int err; + if (!mddev->pers->hot_add_disk) { + printk(KERN_WARNING "md%d: personality does not support diskops!\n", + mdidx(mddev)); + return -EINVAL; + } + rdev = md_import_device(dev, mddev->major_version, + mddev->minor_version); + if (IS_ERR(rdev)) { + printk(KERN_WARNING "md: md_import_device returned %ld\n", PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + rdev->in_sync = 0; /* just to be sure */ + rdev->raid_disk = -1; + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + if (mddev->thread) + md_wakeup_thread(mddev->thread); + return err; + } + + /* otherwise, add_new_disk is only allowed + * for major_version==0 superblocks + */ + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: ADD_NEW_DISK not supported\n", + mdidx(mddev)); + return -EINVAL; } if (!(info->state & (1<in_sync = 0; - bind_rdev_to_array(rdev, mddev); + err = bind_rdev_to_array(rdev, mddev); + if (err) { + export_rdev(rdev); + return err; + } - if (!mddev->persistent) + if (!mddev->persistent) { printk(KERN_INFO "md: nonpersistent superblock ...\n"); + rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + } else + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->size = calc_dev_size(rdev, mddev->chunk_size); - size = calc_dev_size(rdev->bdev, mddev); - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - - if (!mddev->size || (mddev->size > size)) - mddev->size = size; + if (!mddev->size || (mddev->size > rdev->size)) + mddev->size = rdev->size; } return 0; @@ -2076,7 +2115,7 @@ busy: static int hot_add_disk(mddev_t * mddev, dev_t dev) { - int i, err; + int err; unsigned int size; mdk_rdev_t *rdev; @@ -2086,19 +2125,26 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) printk(KERN_INFO "md: trying to hot-add %s to md%d ... \n", partition_name(dev), mdidx(mddev)); + if (mddev->major_version != 0) { + printk(KERN_WARNING "md%d: HOT_ADD may only be used with version-0 superblocks.\n", + mdidx(mddev)); + return -EINVAL; + } if (!mddev->pers->hot_add_disk) { printk(KERN_WARNING "md%d: personality does not support diskops!\n", mdidx(mddev)); return -EINVAL; } - rdev = md_import_device (dev, 0); + rdev = md_import_device (dev, -1, 0); if (IS_ERR(rdev)) { printk(KERN_WARNING "md: error, md_import_device() returned %ld\n", PTR_ERR(rdev)); return -EINVAL; } - size = calc_dev_size(rdev->bdev, mddev); + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + size = calc_dev_size(rdev, mddev->chunk_size); + rdev->size = size; if (size < mddev->size) { printk(KERN_WARNING "md%d: disk size %llu blocks < array size %llu\n", @@ -2115,27 +2161,21 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev) goto abort_export; } rdev->in_sync = 0; + rdev->desc_nr = -1; bind_rdev_to_array(rdev, mddev); /* * The rest should better be atomic, we can have disk failures * noticed in interrupt contexts ... */ - rdev->size = size; - rdev->sb_offset = calc_dev_sboffset(rdev->bdev); - - for (i = mddev->raid_disks; i < mddev->max_disks; i++) - if (find_rdev_nr(mddev,i)==NULL) - break; - if (i == mddev->max_disks) { + if (rdev->desc_nr == mddev->max_disks) { printk(KERN_WARNING "md%d: can not hot-add to full array!\n", mdidx(mddev)); err = -EBUSY; goto abort_unbind_export; } - rdev->desc_nr = i; rdev->raid_disk = -1; md_update_sb(mddev); @@ -2157,9 +2197,37 @@ abort_export: return err; } +/* + * set_array_info is used two different ways + * The original usage is when creating a new array. + * In this usage, raid_disks is > = and it together with + * level, size, not_persistent,layout,chunksize determine the + * shape of the array. + * This will always create an array with a type-0.90.0 superblock. + * The newer usage is when assembling an array. + * In this case raid_disks will be 0, and the major_version field is + * use to determine which style super-blocks are to be found on the devices. + * The minor and patch _version numbers are also kept incase the + * super_block handler wishes to interpret them. + */ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) { + if (info->raid_disks == 0) { + /* just setting version number for superblock loading */ + if (info->major_version < 0 || + info->major_version >= sizeof(super_types)/sizeof(super_types[0]) || + super_types[info->major_version].name == NULL) { + /* maybe try to auto-load a module? */ + printk(KERN_INFO "md: superblock version %d not known\n", + info->major_version); + return -EINVAL; + } + mddev->major_version = info->major_version; + mddev->minor_version = info->minor_version; + mddev->patch_version = info->patch_version; + return 0; + } mddev->major_version = MD_MAJOR_VERSION; mddev->minor_version = MD_MINOR_VERSION; mddev->patch_version = MD_PATCHLEVEL_VERSION; @@ -2180,6 +2248,7 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) mddev->layout = info->layout; mddev->chunk_size = info->chunk_size; + mddev->max_disks = MD_SB_DISKS; /* @@ -2293,9 +2362,11 @@ static int md_ioctl(struct inode *inode, struct file *file, err = -EBUSY; goto abort_unlock; } - if (arg) { + { mdu_array_info_t info; - if (copy_from_user(&info, (void*)arg, sizeof(info))) { + if (!arg) + memset(&info, 0, sizeof(info)); + else if (copy_from_user(&info, (void*)arg, sizeof(info))) { err = -EFAULT; goto abort_unlock; } @@ -3365,7 +3436,7 @@ static void autostart_arrays(void) for (i = 0; i < dev_cnt; i++) { dev_t dev = detected_devices[i]; - rdev = md_import_device(dev,1); + rdev = md_import_device(dev,0, 0); if (IS_ERR(rdev)) { printk(KERN_ALERT "md: could not import %s!\n", partition_name(dev)); -- cgit v1.2.3 From 1f27c73768ee9f6231f62591d975f5f44043bd73 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Fri, 14 Mar 2003 02:09:26 -0800 Subject: [PATCH] md: Add new superblock format for md Superblock format '1' resolves a number of issues with superblock format '0'. It is more dense and can support many more sub-devices. It does not contains un-needed redundancy. It adds a few new useful fields --- drivers/md/md.c | 213 +++++++++++++++++++++++++++++++++++++++++++++- include/linux/raid/md_p.h | 53 ++++++++++++ 2 files changed, 264 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index ca726530cd81..b814b0aefd5b 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -763,6 +763,210 @@ static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) sb->sb_csum = calc_sb_csum(sb); } +/* + * version 1 superblock + */ + +static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) +{ + unsigned int disk_csum, csum; + int size = 256 + sb->max_dev*2; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + csum = csum_partial((void *)sb, size, 0); + sb->sb_csum = disk_csum; + return csum; +} + +static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) +{ + struct mdp_superblock_1 *sb; + int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock. + * It is always aligned to a 4K boundary and + * depeding on minor_version, it can be: + * 0: At least 8K, but less than 12K, from end of device + * 1: At start of device + * 2: 4K from start of device. + */ + switch(minor_version) { + case 0: + sb_offset = rdev->bdev->bd_inode->i_size >> 9; + sb_offset -= 8*2; + sb_offset &= ~(4*2); + /* convert from sectors to K */ + sb_offset /= 2; + break; + case 1: + sb_offset = 0; + break; + case 2: + sb_offset = 4; + break; + default: + return -EINVAL; + } + rdev->sb_offset = sb_offset; + + ret = read_disk_sb(rdev); + if (ret) return ret; + + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || + sb->major_version != cpu_to_le32(1) || + le32_to_cpu(sb->max_dev) > (4096-256)/2 || + le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + sb->feature_map != 0) + return -EINVAL; + + if (calc_sb_1_csum(sb) != sb->sb_csum) { + printk(BAD_CSUM, bdev_partition_name(rdev->bdev)); + return -EINVAL; + } + rdev->preferred_minor = 0xffff; + rdev->data_offset = le64_to_cpu(sb->data_offset); + + if (refdev == 0) + return 1; + else { + __u64 ev1, ev2; + struct mdp_superblock_1 *refsb = + (struct mdp_superblock_1*)page_address(refdev->sb_page); + + if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 || + sb->level != refsb->level || + sb->layout != refsb->layout || + sb->chunksize != refsb->chunksize) { + printk(KERN_WARNING "md: %s has strangely different superblock to %s\n", + bdev_partition_name(rdev->bdev), + bdev_partition_name(refdev->bdev)); + return -EINVAL; + } + ev1 = le64_to_cpu(sb->events); + ev2 = le64_to_cpu(refsb->events); + + if (ev1 > ev2) + return 1; + } + if (minor_version) + rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; + else + rdev->size = rdev->sb_offset; + if (rdev->size < le64_to_cpu(sb->data_size)/2) + return -EINVAL; + rdev->size = le64_to_cpu(sb->data_size)/2; + if (le32_to_cpu(sb->chunksize)) + rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1); + return 0; +} + +static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (mddev->raid_disks == 0) { + mddev->major_version = 1; + mddev->minor_version = 0; + mddev->patch_version = 0; + mddev->persistent = 1; + mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9; + mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1); + mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1); + mddev->level = le32_to_cpu(sb->level); + mddev->layout = le32_to_cpu(sb->layout); + mddev->raid_disks = le32_to_cpu(sb->raid_disks); + mddev->size = (u32)le64_to_cpu(sb->size); + mddev->events = le64_to_cpu(sb->events); + + mddev->recovery_cp = le64_to_cpu(sb->resync_offset); + memcpy(mddev->uuid, sb->set_uuid, 16); + + mddev->max_disks = (4096-256)/2; + } else { + __u64 ev1; + ev1 = le64_to_cpu(sb->events); + ++ev1; + if (ev1 < mddev->events) + return -EINVAL; + } + + if (mddev->level != LEVEL_MULTIPATH) { + int role; + rdev->desc_nr = le32_to_cpu(sb->dev_number); + role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); + switch(role) { + case 0xffff: /* spare */ + rdev->in_sync = 0; + rdev->faulty = 0; + rdev->raid_disk = -1; + break; + case 0xfffe: /* faulty */ + rdev->in_sync = 0; + rdev->faulty = 1; + rdev->raid_disk = -1; + break; + default: + rdev->in_sync = 1; + rdev->faulty = 0; + rdev->raid_disk = role; + break; + } + } + return 0; +} + +static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb; + struct list_head *tmp; + mdk_rdev_t *rdev2; + int max_dev, i; + /* make rdev->sb match mddev and rdev data. */ + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + sb->feature_map = 0; + sb->pad0 = 0; + memset(sb->pad1, 0, sizeof(sb->pad1)); + memset(sb->pad2, 0, sizeof(sb->pad2)); + memset(sb->pad3, 0, sizeof(sb->pad3)); + + sb->utime = cpu_to_le64((__u64)mddev->utime); + sb->events = cpu_to_le64(mddev->events); + if (mddev->in_sync) + sb->resync_offset = cpu_to_le64(mddev->recovery_cp); + else + sb->resync_offset = cpu_to_le64(0); + + max_dev = 0; + ITERATE_RDEV(mddev,rdev2,tmp) + if (rdev2->desc_nr > max_dev) + max_dev = rdev2->desc_nr; + + sb->max_dev = max_dev; + for (i=0; idev_roles[max_dev] = cpu_to_le16(0xfffe); + + ITERATE_RDEV(mddev,rdev2,tmp) { + i = rdev2->desc_nr; + if (rdev2->faulty) + sb->dev_roles[i] = cpu_to_le16(0xfffe); + else if (rdev2->in_sync) + sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk); + else + sb->dev_roles[i] = cpu_to_le16(0xffff); + } + + sb->recovery_offset = cpu_to_le64(0); /* not supported yet */ +} + + struct super_type super_types[] = { [0] = { .name = "0.90.0", @@ -771,9 +975,14 @@ struct super_type super_types[] = { .validate_super = super_90_validate, .sync_super = super_90_sync, }, + [1] = { + .name = "md-1", + .owner = THIS_MODULE, + .load_super = super_1_load, + .validate_super = super_1_validate, + .sync_super = super_1_sync, + }, }; - - static mdk_rdev_t * match_dev_unit(mddev_t *mddev, mdk_rdev_t *dev) { diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index cb002ba3556f..022b607bf9d8 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -173,5 +173,58 @@ static inline __u64 md_event(mdp_super_t *sb) { return (ev<<32)| sb->events_lo; } +/* + * The version-1 superblock : + * All numeric fields are little-endian. + * + * total size: 256 bytes plus 2 per device. + * 1K allows 384 devices. + */ +struct mdp_superblock_1 { + /* constant array information - 128 bytes */ + __u32 magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian */ + __u32 major_version; /* 1 */ + __u32 feature_map; /* 0 for now */ + __u32 pad0; /* always set to 0 when writing */ + + __u8 set_uuid[16]; /* user-space generated. */ + char set_name[32]; /* set and interpreted by user-space */ + + __u64 ctime; /* lo 40 bits are seconds, top 24 are microseconds or 0*/ + __u32 level; /* -4 (multipath), -1 (linear), 0,1,4,5 */ + __u32 layout; /* only for raid5 currently */ + __u64 size; /* used size of component devices, in 512byte sectors */ + + __u32 chunksize; /* in 512byte sectors */ + __u32 raid_disks; + __u8 pad1[128-92]; /* set to 0 when written */ + + /* constant this-device information - 64 bytes */ + __u64 data_offset; /* sector start of data, often 0 */ + __u64 data_size; /* sectors in this device that can be used for data */ + __u64 super_offset; /* sector start of this superblock */ + __u64 recovery_offset;/* sectors before this offset (from data_offset) have been recovered */ + __u32 dev_number; /* permanent identifier of this device - not role in raid */ + __u32 cnt_corrected_read; /* number of read errors that were corrected by re-writing */ + __u8 device_uuid[16]; /* user-space setable, ignored by kernel */ + __u8 pad2[64-56]; /* set to 0 when writing */ + + /* array state information - 64 bytes */ + __u64 utime; /* 40 bits second, 24 btes microseconds */ + __u64 events; /* incremented when superblock updated */ + __u64 resync_offset; /* data before this offset (from data_offset) known to be in sync */ + __u32 sb_csum; /* checksum upto devs[max_dev] */ + __u32 max_dev; /* size of devs[] array to consider */ + __u8 pad3[64-40]; /* set to 0 when writing */ + + /* device state information. Indexed by dev_number. + * 2 bytes per device + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + __u16 dev_roles[0]; /* role in array, or 0xffff for a spare, or 0xfffe for faulty */ +}; + #endif -- cgit v1.2.3 From 5b9d959ad5c4e3f0582883f019dfb89e7797d9c9 Mon Sep 17 00:00:00 2001 From: Jean Tourrilhes Date: Fri, 14 Mar 2003 02:13:27 -0800 Subject: [PATCH] export platform_bus_type --- drivers/base/platform.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index de932ddea39b..f05582c65d09 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -75,5 +75,6 @@ int __init platform_bus_init(void) return bus_register(&platform_bus_type); } +EXPORT_SYMBOL(platform_bus_type); EXPORT_SYMBOL(platform_device_register); EXPORT_SYMBOL(platform_device_unregister); -- cgit v1.2.3 From 4878dffcc885bffeb347f48c386fa134139c14fc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 14 Mar 2003 03:35:27 -0800 Subject: [PATCH] fix OOPS in i2c sysctl registration I had to rewrite the code from scratch to understand what it does, but at least it doesn't OOPS anymore on boot.. --- drivers/i2c/i2c-proc.c | 180 ++++++++++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c index 2b2e752e0769..73bb33815907 100644 --- a/drivers/i2c/i2c-proc.c +++ b/drivers/i2c/i2c-proc.c @@ -35,8 +35,6 @@ #include #include -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr); static int i2c_parse_reals(int *nrels, void *buffer, int bufsize, long *results, int magnitude); static int i2c_write_reals(int nrels, void *buffer, size_t *bufsize, @@ -54,15 +52,6 @@ static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; -static ctl_table sysctl_table[] = { - {CTL_DEV, "dev", NULL, 0, 0555}, - {0}, - {DEV_SENSORS, "sensors", NULL, 0, 0555}, - {0}, - {0, NULL, NULL, 0, 0555}, - {0} -}; - static ctl_table i2c_proc_dev_sensors[] = { {SENSORS_CHIPS, "chips", NULL, 0, 0644, NULL, &i2c_proc_chips, &i2c_sysctl_chips}, @@ -87,36 +76,40 @@ static struct ctl_table_header *i2c_proc_header; (for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for a LM75 chip on the third i2c bus at address 0x4e). name is allocated first. */ -static int i2c_create_name(char **name, const char *prefix, - struct i2c_adapter *adapter, int addr) +static char *generate_name(struct i2c_client *client, const char *prefix) { - char name_buffer[50]; - int id, i, end; - if (i2c_is_isa_adapter(adapter)) + struct i2c_adapter *adapter = client->adapter; + int addr = client->addr; + char name_buffer[50], *name; + + if (i2c_is_isa_adapter(adapter)) { sprintf(name_buffer, "%s-isa-%04x", prefix, addr); - else if (!adapter->algo->smbus_xfer && !adapter->algo->master_xfer) { - /* dummy adapter, generate prefix */ + } else if (adapter->algo->smbus_xfer || adapter->algo->master_xfer) { + int id = i2c_adapter_id(adapter); + if (id < 0) + return ERR_PTR(-ENOENT); + sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); + } else { /* dummy adapter, generate prefix */ + int end, i; + sprintf(name_buffer, "%s-", prefix); end = strlen(name_buffer); - for(i = 0; i < 32; i++) { - if(adapter->algo->name[i] == ' ') + + for (i = 0; i < 32; i++) { + if (adapter->algo->name[i] == ' ') break; name_buffer[end++] = tolower(adapter->algo->name[i]); } + name_buffer[end] = 0; sprintf(name_buffer + end, "-%04x", addr); - } else { - if ((id = i2c_adapter_id(adapter)) < 0) - return -ENOENT; - sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr); - } - *name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); - if (!*name) { - printk (KERN_WARNING "i2c_create_name: not enough memory\n"); - return -ENOMEM; } - strcpy(*name, name_buffer); - return 0; + + name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL); + if (unlikely(!name)) + return ERR_PTR(-ENOMEM); + strcpy(name, name_buffer); + return name; } /* This rather complex function must be called when you want to add an entry @@ -127,93 +120,80 @@ static int i2c_create_name(char **name, const char *prefix, If any driver wants subdirectories within the newly created directory, this function must be updated! */ int i2c_register_entry(struct i2c_client *client, const char *prefix, - ctl_table * ctl_template) + struct ctl_table *leaf) { - int i, res, len, id; - ctl_table *new_table, *client_tbl, *tbl; - char *name; - struct ctl_table_header *new_header; + struct { struct ctl_table root[2], dev[2], sensors[2]; } *tbl; + struct ctl_table_header *hdr; + struct ctl_table *tmp; + const char *name; + int id; + + name = generate_name(client, prefix); + if (IS_ERR(name)) + return PTR_ERR(name); + + for (id = 0; id < SENSORS_ENTRY_MAX; id++) { + if (!i2c_entries[id]) + goto free_slot; + } - if ((res = i2c_create_name(&name, prefix, client->adapter, - client->addr))) return res; + goto out_free_name; - for (id = 0; id < SENSORS_ENTRY_MAX; id++) - if (!i2c_entries[id]) { - break; - } - if (id == SENSORS_ENTRY_MAX) { - kfree(name); - return -ENOMEM; - } + free_slot: + tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); + if (unlikely(!tbl)) + goto out_free_name; + memset(tbl, 0, sizeof(*tbl)); - id += 256; - - len = 0; - while (ctl_template[len].procname) - len++; - if (!(new_table = kmalloc(sizeof(sysctl_table) + sizeof(ctl_table) * (len + 1), - GFP_KERNEL))) { - kfree(name); - return -ENOMEM; - } + for (tmp = leaf; tmp->ctl_name; tmp++) + tmp->extra2 = client; - memcpy(new_table, sysctl_table, sizeof(sysctl_table)); - tbl = new_table; /* sys/ */ - tbl = tbl->child = tbl + 2; /* dev/ */ - tbl = tbl->child = tbl + 2; /* sensors/ */ - client_tbl = tbl->child = tbl + 2; /* XX-chip-YY-ZZ/ */ + tbl->sensors->ctl_name = id+256; + tbl->sensors->procname = name; + tbl->sensors->mode = 0555; + tbl->sensors->child = leaf; - client_tbl->procname = name; - client_tbl->ctl_name = id; - client_tbl->child = client_tbl + 2; + tbl->dev->ctl_name = DEV_SENSORS; + tbl->dev->procname = "sensors"; + tbl->dev->mode = 0555; + tbl->dev->child = tbl->sensors; - /* Next the client sysctls. --km */ - tbl = client_tbl->child; - memcpy(tbl, ctl_template, sizeof(ctl_table) * (len+1)); - for (i = 0; i < len; i++) - tbl[i].extra2 = client; + tbl->root->ctl_name = CTL_DEV; + tbl->root->procname = "dev"; + tbl->root->mode = 0555; + tbl->root->child = tbl->dev; - if (!(new_header = register_sysctl_table(new_table, 0))) { - printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); - kfree(new_table); - kfree(name); - return -EPERM; - } + hdr = register_sysctl_table(tbl->root, 0); + if (unlikely(!hdr)) + goto out_free_tbl; - i2c_entries[id - 256] = new_header; + i2c_entries[id] = hdr; + i2c_clients[id] = client; - i2c_clients[id - 256] = client; + return (id + 256); /* XXX(hch) why?? */ -#ifdef DEBUG - if (!new_header || !new_header->ctl_table || - !new_header->ctl_table->child || - !new_header->ctl_table->child->child || - !new_header->ctl_table->child->child->de ) { - printk - (KERN_ERR "i2c-proc.o: NULL pointer when trying to install fill_inode fix!\n"); - return id; - } -#endif /* DEBUG */ - client_tbl->de->owner = client->driver->owner; - return id; + out_free_tbl: + kfree(tbl); + out_free_name: + kfree(name); + return -ENOMEM; } void i2c_deregister_entry(int id) { - ctl_table *table; - char *temp; + id -= 256; - id -= 256; if (i2c_entries[id]) { - table = i2c_entries[id]->ctl_table; - unregister_sysctl_table(i2c_entries[id]); - /* 2-step kfree needed to keep gcc happy about const points */ - (const char *) temp = table[4].procname; - kfree(temp); - kfree(table); - i2c_entries[id] = NULL; - i2c_clients[id] = NULL; + struct ctl_table_header *hdr = i2c_entries[id]; + struct ctl_table *tbl = hdr->ctl_table; + + unregister_sysctl_table(hdr); + kfree(tbl->child->child->procname); + kfree(tbl); /* actually the whole anonymous struct */ } + + i2c_entries[id] = NULL; + i2c_clients[id] = NULL; } static int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, -- cgit v1.2.3 From 2f43b60032584a4d48e2dbc07f0ebfe09571daaa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 14 Mar 2003 03:35:33 -0800 Subject: [PATCH] fix up the i2c locking changes There was one place where we missed an unlock, in addition some more code cleanups. --- drivers/i2c/i2c-core.c | 81 +++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 238c5a31e4f3..2718e8a7df83 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -313,42 +313,45 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr) int i2c_attach_client(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; - int res = -EBUSY, i; + int i; down(&adapter->list); - if (__i2c_check_addr(client->adapter,client->addr)) + if (__i2c_check_addr(client->adapter, client->addr)) goto out_unlock_list; - for (i = 0; i < I2C_CLIENT_MAX; i++) - if (NULL == adapter->clients[i]) - break; - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING - " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", - client->name); - res = -ENOMEM; - goto out_unlock_list; + for (i = 0; i < I2C_CLIENT_MAX; i++) { + if (!adapter->clients[i]) + goto free_slot; } + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + + out_unlock_list: + up(&adapter->list); + return -EBUSY; + + free_slot: adapter->clients[i] = client; up(&adapter->list); - if (adapter->client_register) - if (adapter->client_register(client)) - printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " + if (adapter->client_register) { + if (adapter->client_register(client)) { + printk(KERN_DEBUG + "i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", - client->addr,adapter->name); - DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", - client->name, adapter->name,i)); + client->addr, adapter->name); + } + } + + DEB(printk(KERN_DEBUG + "i2c-core.o: client [%s] registered to adapter [%s] " + "(pos. %d).\n", client->name, adapter->name, i)); - if(client->flags & I2C_CLIENT_ALLOW_USE) + if (client->flags & I2C_CLIENT_ALLOW_USE) client->usage_count = 0; - return 0; - - out_unlock_list: - up(&adapter->list); - return res; } @@ -363,28 +366,30 @@ int i2c_detach_client(struct i2c_client *client) if (adapter->client_unregister) { res = adapter->client_unregister(client); if (res) { - printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " - "client not detached",client->name); - return res; + printk(KERN_ERR + "i2c-core.o: client_unregister [%s] failed, " + "client not detached", client->name); + goto out; } } down(&adapter->list); for (i = 0; i < I2C_CLIENT_MAX; i++) { - if (client == adapter->clients[i]) - break; + if (client == adapter->clients[i]) { + adapter->clients[i] = NULL; + goto out_unlock; + } } - if (I2C_CLIENT_MAX == i) { - printk(KERN_WARNING " i2c-core.o: unregister_client " - "[%s] not found\n", - client->name); - return -ENODEV; - } else - adapter->clients[i] = NULL; - up(&adapter->list); + printk(KERN_WARNING + " i2c-core.o: unregister_client [%s] not found\n", + client->name); + res = -ENODEV; - return 0; + out_unlock: + up(&adapter->list); + out: + return res; } static int i2c_inc_use_client(struct i2c_client *client) @@ -563,7 +568,7 @@ static int i2cproc_register(struct i2c_adapter *adap, int bus) goto fail; proc_entry->proc_fops = &i2cproc_operations; - proc_entry->owner = THIS_MODULE; + proc_entry->owner = adap->owner; adap->inode = proc_entry->low_ino; return 0; fail: -- cgit v1.2.3 From 95803d5a6a4df0855544c4c9ce69618f906922e0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 14 Mar 2003 03:35:40 -0800 Subject: [PATCH] switch over /proc/bus/i2c to seq_file interface --- drivers/i2c/i2c-core.c | 100 ++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 2718e8a7df83..8c5c06a509d2 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -448,45 +449,7 @@ int i2c_release_client(struct i2c_client *client) return 0; } -/* ---------------------------------------------------- - * The /proc functions - * ---------------------------------------------------- - */ - #ifdef CONFIG_PROC_FS -/* This function generates the output for /proc/bus/i2c */ -static int read_bus_i2c(char *buf, char **start, off_t offset, - int len, int *eof, void *private) -{ - int i; - int nr = 0; - - /* Note that it is safe to write a `little' beyond len. Yes, really. */ - /* Fuck you. Will convert this to seq_file later. --hch */ - - down(&core_lists); - for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) { - if (adapters[i]) { - nr += sprintf(buf+nr, "i2c-%d\t", i); - if (adapters[i]->algo->smbus_xfer) { - if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"smbus/i2c"); - else - nr += sprintf(buf+nr,"smbus "); - } else if (adapters[i]->algo->master_xfer) - nr += sprintf(buf+nr,"i2c "); - else - nr += sprintf(buf+nr,"dummy "); - nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", - adapters[i]->name, - adapters[i]->algo->name); - } - } - up(&core_lists); - - return nr; -} - /* This function generates the output for /proc/bus/i2c-? */ static ssize_t i2cproc_bus_read(struct file *file, char *buf, size_t count, loff_t *ppos) @@ -556,6 +519,50 @@ static struct file_operations i2cproc_operations = { .read = i2cproc_bus_read, }; +/* This function generates the output for /proc/bus/i2c */ +static int bus_i2c_show(struct seq_file *s, void *p) +{ + int i; + + down(&core_lists); + for (i = 0; i < I2C_ADAP_MAX; i++) { + struct i2c_adapter *adapter = adapters[i]; + + if (!adapter) + continue; + + seq_printf(s, "i2c-%d\t", i); + + if (adapter->algo->smbus_xfer) { + if (adapter->algo->master_xfer) + seq_printf(s, "smbus/i2c"); + else + seq_printf(s, "smbus "); + } else if (adapter->algo->master_xfer) + seq_printf(s ,"i2c "); + else + seq_printf(s, "dummy "); + + seq_printf(s, "\t%-32s\t%-32s\n", + adapter->name, adapter->algo->name); + } + up(&core_lists); + + return 0; +} + +static int bus_i2c_open(struct inode *inode, struct file *file) +{ + return single_open(file, bus_i2c_show, NULL); +} + +static struct file_operations bus_i2c_fops = { + .open = bus_i2c_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + }; + static int i2cproc_register(struct i2c_adapter *adap, int bus) { struct proc_dir_entry *proc_entry; @@ -588,15 +595,16 @@ static int __init i2cproc_init(void) { struct proc_dir_entry *proc_bus_i2c; - proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); - if (!proc_bus_i2c) { - printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); - return -ENOENT; - } + proc_bus_i2c = create_proc_entry("i2c", 0, proc_bus); + if (!proc_bus_i2c) + goto fail; + proc_bus_i2c->proc_fops = &bus_i2c_fops; + proc_bus_i2c->owner = THIS_MODULE; + return 0; - proc_bus_i2c->read_proc = &read_bus_i2c; - proc_bus_i2c->owner = THIS_MODULE; - return 0; + fail: + printk(KERN_ERR "i2c-core.o: Could not create /proc/bus/i2c"); + return -ENOENT; } static void __exit i2cproc_cleanup(void) -- cgit v1.2.3 From 306fda03757fc4b826a31c4faa118530358ff7b2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 14 Mar 2003 06:10:37 -0600 Subject: [PATCH] remove scsi_eh_retry_cmd Compile warnings are useful... --- drivers/scsi/scsi_error.c | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index eec12d6a61ba..b9c826ed12eb 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -596,28 +596,6 @@ static int scsi_request_sense(struct scsi_cmnd *scmd) return rtn; } -/** - * scsi_eh_retry_cmd - Retry the original command - * @scmd: Original failed SCSI cmd. - * - * Notes: - * This function will *not* return until the command either times out, - * or it completes. - **/ -static int scsi_eh_retry_cmd(struct scsi_cmnd *scmd) -{ - int rtn = SUCCESS; - - for (; scmd->retries < scmd->allowed; scmd->retries++) { - scsi_setup_cmd_retry(scmd); - rtn = scsi_send_eh_cmnd(scmd, scmd->timeout_per_command); - if (rtn != NEEDS_RETRY) - break; - } - - return rtn; -} - /** * scsi_eh_finish_cmd - Handle a cmd that eh is finished with. * @scmd: Original SCSI cmd that eh has finished. -- cgit v1.2.3 From 5f99bd9a4ad3c3f4829f2482e886229e59446f61 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 14 Mar 2003 06:11:09 -0600 Subject: [PATCH] fix possible NULL pointer dereference in scsi_scan.c If the sdev allocation fails and q is non-null we could dereference sdev->request_queue. While at it reformat the function to use goto-based cleanup - that's much easier to parse. --- drivers/scsi/scsi_scan.c | 139 +++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 65 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 185fec2e14c8..20115e69dc05 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -385,83 +385,92 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, struct scsi_device *sdev, *device; sdev = kmalloc(sizeof(*sdev), GFP_ATOMIC); - if (sdev != NULL) { - memset(sdev, 0, sizeof(Scsi_Device)); - sdev->vendor = scsi_null_device_strs; - sdev->model = scsi_null_device_strs; - sdev->rev = scsi_null_device_strs; - sdev->host = shost; - sdev->id = id; - sdev->lun = lun; - sdev->channel = channel; - sdev->online = TRUE; - INIT_LIST_HEAD(&sdev->siblings); - INIT_LIST_HEAD(&sdev->same_target_siblings); - INIT_LIST_HEAD(&sdev->cmd_list); - spin_lock_init(&sdev->list_lock); - /* - * Some low level driver could use device->type - */ - sdev->type = -1; - /* - * Assume that the device will have handshaking problems, - * and then fix this field later if it turns out it - * doesn't - */ - sdev->borken = 1; + if (!sdev) + goto out; - if (!q || *q == NULL) { - sdev->request_queue = scsi_alloc_queue(shost); - if (!sdev->request_queue) - goto out_bail; - } else { - sdev->request_queue = *q; - *q = NULL; - } + memset(sdev, 0, sizeof(*sdev)); + sdev->vendor = scsi_null_device_strs; + sdev->model = scsi_null_device_strs; + sdev->rev = scsi_null_device_strs; + sdev->host = shost; + sdev->id = id; + sdev->lun = lun; + sdev->channel = channel; + sdev->online = TRUE; + INIT_LIST_HEAD(&sdev->siblings); + INIT_LIST_HEAD(&sdev->same_target_siblings); + INIT_LIST_HEAD(&sdev->cmd_list); + spin_lock_init(&sdev->list_lock); - sdev->request_queue->queuedata = sdev; - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); - init_waitqueue_head(&sdev->scpnt_wait); + /* + * Some low level driver could use device->type + */ + sdev->type = -1; - if (shost->hostt->slave_alloc) - if (shost->hostt->slave_alloc(sdev)) { - goto out_bail; - } - /* - * If there are any same target siblings, add this to the - * sibling list - */ - list_for_each_entry(device, &shost->my_devices, siblings) { - if(device->id == sdev->id && - device->channel == sdev->channel) { - list_add_tail(&sdev->same_target_siblings, - &device->same_target_siblings); - sdev->scsi_level = device->scsi_level; - break; - } + /* + * Assume that the device will have handshaking problems, + * and then fix this field later if it turns out it + * doesn't + */ + sdev->borken = 1; + + if (!q || *q == NULL) { + sdev->request_queue = scsi_alloc_queue(shost); + if (!sdev->request_queue) + goto out_free_dev; + } else { + sdev->request_queue = *q; + *q = NULL; + } + + sdev->request_queue->queuedata = sdev; + scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + init_waitqueue_head(&sdev->scpnt_wait); + + if (shost->hostt->slave_alloc) { + if (shost->hostt->slave_alloc(sdev)) + goto out_free_queue; + } + + /* + * If there are any same target siblings, add this to the + * sibling list + */ + list_for_each_entry(device, &shost->my_devices, siblings) { + if (device->id == sdev->id && + device->channel == sdev->channel) { + list_add_tail(&sdev->same_target_siblings, + &device->same_target_siblings); + sdev->scsi_level = device->scsi_level; + break; } - /* - * If there wasn't another lun already configured at this - * target, then default this device to SCSI_2 until we - * know better - */ - if(!sdev->scsi_level) - sdev->scsi_level = SCSI_2; - /* - * Add it to the end of the shost->my_devices list. - */ - list_add_tail(&sdev->siblings, &shost->my_devices); - return (sdev); } -out_bail: - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + + /* + * If there wasn't another lun already configured at this + * target, then default this device to SCSI_2 until we + * know better + */ + if (!sdev->scsi_level) + sdev->scsi_level = SCSI_2; + + /* + * Add it to the end of the shost->my_devices list. + */ + list_add_tail(&sdev->siblings, &shost->my_devices); + return sdev; + +out_free_queue: if (q && sdev->request_queue) { *q = sdev->request_queue; sdev->request_queue = NULL; } else if (sdev->request_queue) scsi_free_queue(sdev->request_queue); +out_free_dev: kfree(sdev); +out: + printk(ALLOC_FAILURE_MSG, __FUNCTION__); return NULL; } -- cgit v1.2.3 From f1b28fd9e7827f4806aebe8fb0d8c3ff5cfc3e9c Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 14 Mar 2003 06:35:45 -0600 Subject: scsi_softirq queue is now list_head, eliminate bh_next The following patch gets rid of softscsi_data struct and array for the more manageable static struct list_head done_q[NR_CPUS] __cacheline_aligned; Thus, scsi_cmnd::bh_next is eliminated, since it was used only in the scsi softirq processing code. The comments are updated. 80 chars per line for the affected functions: scsi_done() and scsi_softirq(). Eliminated is the double loop in scsi_softirq() -- this is better handled in do_softirq() and gives the system a ``breather''. (There are pros and cons for either side and if you guys think that it was better with the double loop, I'll change it and resubmit the patch.) --- drivers/scsi/scsi.c | 233 ++++++++++++++++++++++-------------------------- drivers/scsi/scsi.h | 3 +- drivers/scsi/scsi_lib.c | 1 - 3 files changed, 109 insertions(+), 128 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 5632d669ca7a..0cbfd0c762bb 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -96,12 +96,7 @@ unsigned long scsi_pid; Scsi_Cmnd *last_cmnd; static unsigned long serial_number; -struct softscsi_data { - Scsi_Cmnd *head; - Scsi_Cmnd *tail; -}; - -static struct softscsi_data softscsi_data[NR_CPUS] __cacheline_aligned; +static struct list_head done_q[NR_CPUS] __cacheline_aligned; /* * List of all highlevel drivers. @@ -637,79 +632,60 @@ void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) } /** - * scsi_done - Mark this command as done - * @SCpnt: The SCSI Command which we think we've completed. - * - * This function is the mid-level interrupt routine, which decides how - * to handle error conditions. Each invocation of this function must - * do one and *only* one of the following: + * scsi_done - Enqueue the finished SCSI command into the done queue. + * @cmd: The SCSI Command for which a low-level device driver (LLDD) gives + * ownership back to SCSI Core -- i.e. the LLDD has finished with it. * - * 1) Insert command in BH queue. - * 2) Activate error handler for host. + * This function is the mid-level's (SCSI Core) interrupt routine, which + * regains ownership of the SCSI command (de facto) from a LLDD, and enqueues + * the command to the done queue for further processing. * - * There is no longer a problem with stack overflow. Interrupts queue - * Scsi_Cmnd on a per-CPU queue and the softirq handler removes them - * from the queue one at a time. + * This is the producer of the done queue who enqueues at the tail. * - * This function is sometimes called from interrupt context, but sometimes - * from task context. + * This function is interrupt context safe. */ -void scsi_done(Scsi_Cmnd * SCpnt) +void scsi_done(struct scsi_cmnd *cmd) { + int cpu; unsigned long flags; - int cpu, tstatus; - struct softscsi_data *queue; + struct list_head *pdone_q; /* * We don't have to worry about this one timing out any more. - */ - tstatus = scsi_delete_timer(SCpnt); - - /* - * If we are unable to remove the timer, it means that the command - * has already timed out. In this case, we have no choice but to + * If we are unable to remove the timer, then the command + * has already timed out. In which case, we have no choice but to * let the timeout function run, as we have no idea where in fact * that function could really be. It might be on another processor, * etc, etc. */ - if (!tstatus) { + if (!scsi_delete_timer(cmd)) return; - } /* Set the serial numbers back to zero */ - SCpnt->serial_number = 0; - SCpnt->serial_number_at_timeout = 0; - SCpnt->state = SCSI_STATE_BHQUEUE; - SCpnt->owner = SCSI_OWNER_BH_HANDLER; - SCpnt->bh_next = NULL; + cmd->serial_number = 0; + cmd->serial_number_at_timeout = 0; + cmd->state = SCSI_STATE_BHQUEUE; + cmd->owner = SCSI_OWNER_BH_HANDLER; /* - * Next, put this command in the softirq queue. - * - * This is a per-CPU queue, so we just disable local interrupts + * Next, enqueue the command into the done queue. + * It is a per-CPU queue, so we just disable local interrupts * and need no spinlock. */ - local_irq_save(flags); cpu = smp_processor_id(); - queue = &softscsi_data[cpu]; - - if (!queue->head) { - queue->head = SCpnt; - queue->tail = SCpnt; - } else { - queue->tail->bh_next = SCpnt; - queue->tail = SCpnt; - } - + pdone_q = &done_q[cpu]; + list_add_tail(&cmd->eh_entry, pdone_q); cpu_raise_softirq(cpu, SCSI_SOFTIRQ); local_irq_restore(flags); } /** - * scsi_softirq - Perform post-interrupt handling for completed commands + * scsi_softirq - Perform post-interrupt processing of finished SCSI commands. + * + * This is the consumer of the done queue. * * This is called with all interrupts enabled. This should reduce * interrupt latency, stack depth, and reentrancy of the low-level @@ -717,88 +693,92 @@ void scsi_done(Scsi_Cmnd * SCpnt) */ static void scsi_softirq(struct softirq_action *h) { - int cpu = smp_processor_id(); - struct softscsi_data *queue = &softscsi_data[cpu]; + LIST_HEAD(local_q); - while (queue->head) { - Scsi_Cmnd *SCpnt, *SCnext; + local_irq_disable(); + list_splice_init(&done_q[smp_processor_id()], &local_q); + local_irq_enable(); - local_irq_disable(); - SCpnt = queue->head; - queue->head = NULL; - local_irq_enable(); + while (!list_empty(&local_q)) { + struct scsi_cmnd *cmd = list_entry(local_q.next, + struct scsi_cmnd, eh_entry); + list_del_init(&cmd->eh_entry); - for (; SCpnt; SCpnt = SCnext) { - SCnext = SCpnt->bh_next; + switch (scsi_decide_disposition(cmd)) { + case SUCCESS: + /* + * Add to BH queue. + */ + SCSI_LOG_MLCOMPLETE(3, + printk("Command finished %d %d " + "0x%x\n", + cmd->device->host->host_busy, + cmd->device->host->host_failed, + cmd->result)); + + scsi_finish_command(cmd); + break; + case NEEDS_RETRY: + /* + * We only come in here if we want to retry a + * command. The test to see whether the + * command should be retried should be keeping + * track of the number of tries, so we don't + * end up looping, of course. + */ + SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry " + "%d %d 0x%x\n", + cmd->device->host->host_busy, + cmd->device->host->host_failed, + cmd->result)); - switch (scsi_decide_disposition(SCpnt)) { - case SUCCESS: - /* - * Add to BH queue. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command finished %d %d 0x%x\n", SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed, - SCpnt->result)); + scsi_retry_command(cmd); + break; + case ADD_TO_MLQUEUE: + /* + * This typically happens for a QUEUE_FULL + * message - typically only when the queue + * depth is only approximate for a given + * device. Adding a command to the queue for + * the device will prevent further commands + * from being sent to the device, so we + * shouldn't end up with tons of things being + * sent down that shouldn't be. + */ + SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as " + "device queue full, " + "put on ml queue %p\n", + cmd)); + scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); + break; + default: + /* + * Here we have a fatal error of some sort. + * Turn it over to the error handler. + */ + SCSI_LOG_MLCOMPLETE(3, + printk("Command failed %p %x " + "busy=%d failed=%d\n", + cmd, cmd->result, + cmd->device->host->host_busy, + cmd->device->host->host_failed)); - scsi_finish_command(SCpnt); - break; - case NEEDS_RETRY: - /* - * We only come in here if we want to retry a - * command. The test to see whether the - * command should be retried should be keeping - * track of the number of tries, so we don't - * end up looping, of course. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command needs retry %d %d 0x%x\n", SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed, SCpnt->result)); - - scsi_retry_command(SCpnt); - break; - case ADD_TO_MLQUEUE: - /* - * This typically happens for a QUEUE_FULL - * message - typically only when the queue - * depth is only approximate for a given - * device. Adding a command to the queue for - * the device will prevent further commands - * from being sent to the device, so we - * shouldn't end up with tons of things being - * sent down that shouldn't be. - */ - SCSI_LOG_MLCOMPLETE(3, printk("Command rejected as device queue full, put on ml queue %p\n", - SCpnt)); - scsi_queue_insert(SCpnt, SCSI_MLQUEUE_DEVICE_BUSY); - break; - default: - /* - * Here we have a fatal error of some sort. - * Turn it over to the error handler. - */ - SCSI_LOG_MLCOMPLETE(3, - printk("Command failed %p %x busy=%d failed=%d\n", - SCpnt, SCpnt->result, - SCpnt->device->host->host_busy, - SCpnt->device->host->host_failed)); + /* + * Dump the sense information too. + */ + if ((status_byte(cmd->result)&CHECK_CONDITION) != 0) { + SCSI_LOG_MLCOMPLETE(3, print_sense("bh", cmd)); + } + if (!scsi_eh_scmd_add(cmd, 0)) { /* - * Dump the sense information too. + * We only get here if the error + * recovery thread has died. */ - if ((status_byte(SCpnt->result) & CHECK_CONDITION) != 0) { - SCSI_LOG_MLCOMPLETE(3, print_sense("bh", SCpnt)); - } - - if (!scsi_eh_scmd_add(SCpnt, 0)) - { - /* - * We only get here if the error - * recovery thread has died. - */ - scsi_finish_command(SCpnt); - } - } /* switch */ - } /* for(; SCpnt...) */ - } /* while(queue->head) */ + scsi_finish_command(cmd); + } + } + } } /* @@ -1481,7 +1461,7 @@ __setup("scsi_default_dev_flags=", setup_scsi_default_dev_flags); static int __init init_scsi(void) { - int error; + int error, i; error = scsi_init_queue(); if (error) @@ -1496,6 +1476,9 @@ static int __init init_scsi(void) if (error) goto cleanup_devlist; + for (i = 0; i < NR_CPUS; i++) + INIT_LIST_HEAD(&done_q[i]); + scsi_host_init(); devfs_mk_dir(NULL, "scsi", NULL); open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL); diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 12d9783b9c77..96f92ae21c32 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -741,8 +741,7 @@ struct scsi_cmnd { * abort, etc are in process. */ unsigned volatile char internal_timeout; - struct scsi_cmnd *bh_next; /* To enumerate the commands waiting - to be processed. */ + unsigned char cmd_len; unsigned char old_cmd_len; unsigned char sc_data_direction; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 5ee88b47014b..a06341945483 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -125,7 +125,6 @@ int scsi_queue_insert(struct scsi_cmnd *cmd, int reason) */ cmd->state = SCSI_STATE_MLQUEUE; cmd->owner = SCSI_OWNER_MIDLEVEL; - cmd->bh_next = NULL; /* * Decrement the counters, since these commands are no longer -- cgit v1.2.3 From 7fe8f35f318297dce7e35333a692c83b0630f0ef Mon Sep 17 00:00:00 2001 From: Willem Riede Date: Fri, 14 Mar 2003 06:41:24 -0600 Subject: fix jiffies compare warning in osst On 2003.03.11 14:13 Christoph Hellwig wrote: > > --- 1.39/drivers/scsi/osst.c Sun Feb 2 17:50:23 2003 > +++ edited/drivers/scsi/osst.c Mon Mar 10 14:35:46 2003 > @@ -777,7 +777,7 @@ > #define OSST_POLL_PER_SEC 10 > static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int curr, int minlast, int to) > { > - long startwait = jiffies; > + unsigned long startwait = jiffies; > char * name = tape_name(STp); > #if DEBUG > char notyetprinted = 1; > @@ -1288,7 +1288,7 @@ > int logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num) > - (nframes + pending - 1) * blks_per_frame; > char * name = tape_name(STp); > - long startwait = jiffies; > + unsigned long startwait = jiffies; > #if DEBUG > int dbg = debugging; > #endif > @@ -1477,7 +1477,7 @@ > int expected = 0; > int attempts = 1000 / skip; > int flag = 1; > - long startwait = jiffies; > + unsigned long startwait = jiffies; > #if DEBUG > int dbg = debugging; > #endif > - There are five functions that use jiffies. You fixed three of them. If this change is done (and that's fine with me) it should be done with this patch: --- drivers/scsi/osst.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 1c5ca7e8ad02..8ccfb9e3642b 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -606,7 +606,7 @@ static int osst_wait_ready(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsigned { unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request * SRpnt; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; char * name = tape_name(STp); @@ -673,7 +673,7 @@ static int osst_wait_for_medium(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, unsi { unsigned char cmd[MAX_COMMAND_SIZE]; Scsi_Request * SRpnt; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; char * name = tape_name(STp); @@ -777,8 +777,8 @@ static int osst_flush_drive_buffer(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt) #define OSST_POLL_PER_SEC 10 static int osst_wait_frame(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int curr, int minlast, int to) { - long startwait = jiffies; - char * name = tape_name(STp); + unsigned long startwait = jiffies; + char * name = tape_name(STp); #if DEBUG char notyetprinted = 1; #endif @@ -1288,7 +1288,7 @@ static int osst_read_back_buffer_and_rewrite(OS_Scsi_Tape * STp, Scsi_Request ** int logical_blk_num = ntohl(STp->buffer->aux->logical_blk_num) - (nframes + pending - 1) * blks_per_frame; char * name = tape_name(STp); - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; #endif @@ -1477,7 +1477,7 @@ static int osst_reposition_and_retry(OS_Scsi_Tape * STp, Scsi_Request ** aSRpnt, int expected = 0; int attempts = 1000 / skip; int flag = 1; - long startwait = jiffies; + unsigned long startwait = jiffies; #if DEBUG int dbg = debugging; #endif -- cgit v1.2.3 From d685cc6d9abb35e979a606673a8029bcb5aa9286 Mon Sep 17 00:00:00 2001 From: Alex Tomas Date: Fri, 14 Mar 2003 06:43:43 -0600 Subject: [PATCH] Re: hot scsi disk resize Hi! Here is new version of the patch. All procfs-related stuff has been removed. One may rescan device size writing something to /sysfs/...//rescan: root@zefir:~# echo 1 >/sysfs/bus/scsi/devices/0\:0\:1\:0/rescan root@zefir:~# dmesg scsi0:A:1:0: Tagged Queuing enabled. Depth 64 scsi: host 0 channel 0 id 1 lun16384 has a LUN larger than allowed by the host adapter SCSI device sda: 2097152 512-byte hdwr sectors (1074 MB) SCSI device sda: drive cache: write through sda: unknown partition table Attached scsi disk sda at scsi0, channel 0, id 1, lun 0 SCSI device sda: 125829120 512-byte hdwr sectors (64425 MB) root@zefir:~# --- drivers/scsi/hosts.h | 1 + drivers/scsi/scsi.c | 15 +++++++++++++++ drivers/scsi/scsi_sysfs.c | 20 ++++++++++++++++++++ drivers/scsi/sd.c | 37 ++++++++++++++++++++++++++++++++++++- fs/block_dev.c | 2 ++ 5 files changed, 74 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index c8098010e86b..f5f428711b34 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -549,6 +549,7 @@ struct Scsi_Device_Template void (*detach)(Scsi_Device *); int (*init_command)(Scsi_Cmnd *); /* Used by new queueing code. Selects command for blkdevs */ + void (*rescan)(Scsi_Device *); struct device_driver scsi_driverfs_driver; }; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 0cbfd0c762bb..78577c181884 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -1264,6 +1264,21 @@ void scsi_detach_device(struct scsi_device *sdev) up_read(&scsi_devicelist_mutex); } +void scsi_rescan_device(struct scsi_device *sdev) +{ + struct Scsi_Device_Template *sdt; + + down_read(&scsi_devicelist_mutex); + list_for_each_entry(sdt, &scsi_devicelist, list) { + if (!try_module_get(sdt->module)) + continue; + if (*sdt->rescan) + (*sdt->rescan)(sdev); + module_put(sdt->module); + } + up_read(&scsi_devicelist_mutex); +} + int scsi_device_get(struct scsi_device *sdev) { if (!try_module_get(sdev->host->hostt->module)) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index eade9fd9b026..4ecaa390ebdc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -266,6 +266,25 @@ sdev_rd_attr (model, "%.16s\n"); sdev_rd_attr (rev, "%.4s\n"); sdev_rw_attr_bit (online); +static ssize_t +show_rescan_field (struct device *dev, char *buf) +{ + return 0; +} + +static ssize_t +store_rescan_field (struct device *dev, const char *buf, size_t count) +{ + int ret = ENODEV; + struct scsi_device *sdev; + sdev = to_scsi_device(dev); + if (sdev) + ret = scsi_rescan_device(sdev); + return ret; +} + +static DEVICE_ATTR(rescan, S_IRUGO | S_IWUSR, show_rescan_field, store_rescan_field) + static struct device_attribute * const sdev_attrs[] = { &dev_attr_device_blocked, &dev_attr_queue_depth, @@ -276,6 +295,7 @@ static struct device_attribute * const sdev_attrs[] = { &dev_attr_model, &dev_attr_rev, &dev_attr_online, + &dev_attr_rescan, }; /** diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 12d4a6839639..eadc8315551a 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -93,10 +93,12 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt); static int sd_attach(struct scsi_device *); static void sd_detach(struct scsi_device *); +static void sd_rescan(struct scsi_device *); static int sd_init_command(struct scsi_cmnd *); static int sd_synchronize_cache(struct scsi_disk *, int); static int sd_notifier(struct notifier_block *, unsigned long, void *); - +static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, + struct scsi_request *SRpnt, unsigned char *buffer); static struct notifier_block sd_notifier_block = {sd_notifier, NULL, 0}; static struct Scsi_Device_Template sd_template = { @@ -106,6 +108,7 @@ static struct Scsi_Device_Template sd_template = { .scsi_type = TYPE_DISK, .attach = sd_attach, .detach = sd_detach, + .rescan = sd_rescan, .init_command = sd_init_command, .scsi_driverfs_driver = { .name = "sd", @@ -629,6 +632,38 @@ not_present: return 1; } +static void sd_rescan(struct scsi_device * sdp) +{ + unsigned char *buffer; + struct scsi_disk *sdkp = sd_find_by_sdev(sdp); + struct gendisk *gd; + struct scsi_request *SRpnt; + + if (!sdkp || sdp->online == FALSE || !sdkp->media_present) + return; + + gd = sdkp->disk; + + SCSI_LOG_HLQUEUE(3, printk("sd_rescan: disk=%s\n", gd->disk_name)); + + SRpnt = scsi_allocate_request(sdp); + if (!SRpnt) { + printk(KERN_WARNING "(sd_rescan:) Request allocation " + "failure.\n"); + return; + } + + if (sdkp->device->host->unchecked_isa_dma) + buffer = kmalloc(512, GFP_DMA); + else + buffer = kmalloc(512, GFP_KERNEL); + + sd_read_capacity(sdkp, gd->disk_name, SRpnt, buffer); + set_capacity(gd, sdkp->capacity); + scsi_release_request(SRpnt); + kfree(buffer); +} + static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); diff --git a/fs/block_dev.c b/fs/block_dev.c index d46f44fb0d47..b3dc2d6e0aa6 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -573,6 +573,8 @@ static int do_open(struct block_device *bdev, struct inode *inode, struct file * up(&whole->bd_sem); } } else { + if (!part) + bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); put_disk(disk); module_put(owner); if (bdev->bd_contains == bdev) { -- cgit v1.2.3 From ca636121d7cef833879607a8c89cd6a558fe8e63 Mon Sep 17 00:00:00 2001 From: Mark Haverkamp Date: Fri, 14 Mar 2003 06:45:16 -0600 Subject: [PATCH] aacraid driver for 2.5 This changes the cmd_per_lun element of the aacraid Scsi_Host_Template to 1. The larger number is not needed and exceeds the depth limit for scsi_adjust_queue_depth. Also updated struct initializers. --- drivers/scsi/aacraid/linit.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 95af5d5f81d0..9f5ace7ae222 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -679,27 +679,26 @@ static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int */ static Scsi_Host_Template driver_template = { - module: THIS_MODULE, - name: "AAC", - proc_info: aac_procinfo, - detect: aac_detect, - release: aac_release, - info: aac_driverinfo, - ioctl: aac_ioctl, - queuecommand: aac_queuecommand, - bios_param: aac_biosparm, - slave_configure: aac_slave_configure, - can_queue: AAC_NUM_IO_FIB, - this_id: 16, - sg_tablesize: 16, - max_sectors: 128, - cmd_per_lun: AAC_NUM_IO_FIB, - eh_abort_handler: aac_eh_abort, - eh_device_reset_handler:aac_eh_device_reset, - eh_bus_reset_handler: aac_eh_bus_reset, - eh_host_reset_handler: aac_eh_reset, - - use_clustering: ENABLE_CLUSTERING, + .module = THIS_MODULE, + .name = "AAC", + .proc_info = aac_procinfo, + .detect = aac_detect, + .release = aac_release, + .info = aac_driverinfo, + .ioctl = aac_ioctl, + .queuecommand = aac_queuecommand, + .bios_param = aac_biosparm, + .slave_configure = aac_slave_configure, + .can_queue = AAC_NUM_IO_FIB, + .this_id = 16, + .sg_tablesize = 16, + .max_sectors = 128, + .cmd_per_lun = 1, + .eh_abort_handler = aac_eh_abort, + .eh_device_reset_handler = aac_eh_device_reset, + .eh_bus_reset_handler = aac_eh_bus_reset, + .eh_host_reset_handler = aac_eh_reset, + .use_clustering = ENABLE_CLUSTERING, }; #include "scsi_module.c" -- cgit v1.2.3 From 16b7fc6628f82a41a5ff5824e9677aba0fc1ba01 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Fri, 14 Mar 2003 06:48:23 -0600 Subject: [PATCH] scsi_debug in 2.5.64 Here is a second attempt to patch scsi_debug in 2.5.64 . My reference version was out of sync in my previous posting. Changelog: - add recovered error injection (this is a bit different to the patch proposed by Kurt Garloff) - fix flakiness in scsi_cmnd::result when errors are being injected - fix medium error injection - make "every_nth" writeable in sysfs - small re-arrangement of error flags in "opts" - clean up some of the naming Updated http://www.torque.net/sg/sdebug25.html This patch does not include Mike Anderson's sysfs probe() cleanup. In 2.5.64 scsi error handling is flaky and sysfs is especially flaky in 2.5.64-bk3. I'll send some finding to the list when things stabilize a bit. [For anyone who is bored, try following the tortured sequence of scsi commands generated by the block/sd/mid-level layers in response to a persistent medium error.] --- drivers/scsi/scsi_debug.c | 150 +++++++++++++++++++++++++++++----------------- 1 file changed, 95 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index e16e70bcad90..fdc13f3d70d5 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -54,7 +54,7 @@ #include "scsi_debug.h" -static const char * scsi_debug_version_str = "Version: 1.67 (20021221)"; +static const char * scsi_debug_version_str = "Version: 1.68 (20030309)"; #ifndef SCSI_CMD_READ_16 #define SCSI_CMD_READ_16 0x88 @@ -68,7 +68,7 @@ static const char * scsi_debug_version_str = "Version: 1.67 (20021221)"; /* Default values for driver parameters */ #define DEF_NUM_DEVS 1 #define DEF_DEV_SIZE_MB 8 -#define DEF_EVERY_NTH 100 +#define DEF_EVERY_NTH 0 #define DEF_DELAY 1 #define DEF_MAX_LUNS 2 #define DEF_SCSI_LEVEL 3 @@ -80,9 +80,17 @@ static const char * scsi_debug_version_str = "Version: 1.67 (20021221)"; /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_MEDIUM_ERR 2 -#define SCSI_DEBUG_OPT_EVERY_NTH 4 +#define SCSI_DEBUG_OPT_TIMEOUT 4 +#define SCSI_DEBUG_OPT_RECOVERED_ERR 8 +/* When "every_nth" > 0 then modulo "every_nth" commands: + * - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set + * - a RECOVERED_ERROR is simulated on successful read and write + * commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set. + */ -#define OPT_MEDIUM_ERR_ADDR 0x1234 +/* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this + * sector on read commands: */ +#define OPT_MEDIUM_ERR_ADDR 0x1234 /* that's sector 4660 in decimal */ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; static int scsi_debug_num_devs = DEF_NUM_DEVS; @@ -161,6 +169,9 @@ static struct device_driver sdebug_driverfs_driver = { .devclass = &shost_devclass, }; +static const int check_condition_result = + (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + /* function declarations */ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip); @@ -222,6 +233,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) unsigned long capac; struct sdebug_dev_info * devip = NULL; unsigned char * sbuff; + int inj_recovered = 0; if (done == NULL) return 0; /* assume mid level reprocessing command */ @@ -255,11 +267,13 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); - if ((SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) && - (scsi_debug_every_nth > 0) && + if ((scsi_debug_every_nth > 0) && (++scsi_debug_cmnd_count >= scsi_debug_every_nth)) { scsi_debug_cmnd_count =0; - return 0; /* ignore command causing timeout */ + if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts) + return 0; /* ignore command causing timeout */ + else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts) + inj_recovered = 1; /* to reads and writes below */ } switch (*cmd) { @@ -269,8 +283,8 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) case REQUEST_SENSE: /* mandatory */ /* Since this driver indicates autosense by placing the * sense buffer in the scsi_cmnd structure in the response - * (when CHECK_CONDITION is set), the mid level shouldn't - * need to call REQUEST_SENSE */ + * (when SAM_STAT_CHECK_CONDITION is set), the mid level + * shouldn't need to call REQUEST_SENSE */ if (devip) { sbuff = devip->sense_buff; memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ? @@ -355,6 +369,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) num = cmd[4]; } errsts = resp_read(SCpnt, upper_blk, block, num, devip); + if (inj_recovered && (0 == errsts)) { + mk_sense_buffer(devip, RECOVERED_ERROR, 0x5d, 0, 14); + errsts = check_condition_result; + } break; case REPORT_LUNS: errsts = resp_report_luns(cmd, buff, bufflen, devip); @@ -388,6 +406,10 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) num = cmd[4]; } errsts = resp_write(SCpnt, upper_blk, block, num, devip); + if (inj_recovered && (0 == errsts)) { + mk_sense_buffer(devip, RECOVERED_ERROR, 0x5d, 0, 14); + errsts = check_condition_result; + } break; case MODE_SENSE: case MODE_SENSE_10: @@ -400,7 +422,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) if ((errsts = check_reset(SCpnt, devip))) break; mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x20, 0, 14); - errsts = (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + errsts = check_condition_result; break; } return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay); @@ -420,7 +442,7 @@ static int check_reset(struct scsi_cmnd * SCpnt, struct sdebug_dev_info * devip) if (devip->reset) { devip->reset = 0; mk_sense_buffer(devip, UNIT_ATTENTION, 0x29, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } return 0; } @@ -481,7 +503,7 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, arr[0] = pq_pdt; if (0x2 & cmd[1]) { /* CMDDT bit set */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } else if (0x1 & cmd[1]) { /* EVPD bit set */ int dev_id_num, len; char dev_id_str[6]; @@ -506,7 +528,7 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, } else { /* Illegal request, invalid field in cdb */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } memcpy(buff, arr, min_len); return 0; @@ -619,7 +641,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); if (0x3 == pcontrol) { /* Saving values not supported */ mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x39, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; if (msense_6) { @@ -662,7 +684,7 @@ static int resp_mode_sense(unsigned char * cmd, int target, break; default: mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if (msense_6) arr[0] = offset - 1; @@ -686,14 +708,14 @@ static int resp_read(struct scsi_cmnd * SCpnt, int upper_blk, int block, if (upper_blk || (block + num > sdebug_capacity)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && - (block >= OPT_MEDIUM_ERR_ADDR) && - (block < (OPT_MEDIUM_ERR_ADDR + num))) { + (block <= OPT_MEDIUM_ERR_ADDR) && + ((block + num) > OPT_MEDIUM_ERR_ADDR)) { mk_sense_buffer(devip, MEDIUM_ERROR, 0x11, 0, 14); /* claim unrecoverable read error */ - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } read_lock_irqsave(&atomic_rw, iflags); sgcount = 0; @@ -735,7 +757,7 @@ static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, if (upper_blk || (block + num > sdebug_capacity)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } write_lock_irqsave(&atomic_rw, iflags); @@ -776,7 +798,7 @@ static int resp_report_luns(unsigned char * cmd, unsigned char * buff, alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); if ((alloc_len < 16) || (select_report > 2)) { mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); - return (DRIVER_SENSE << 24) | (CHECK_CONDITION << 1); + return check_condition_result; } if (bufflen > 3) { lun_cnt = min((int)(bufflen / sizeof(ScsiLun)), @@ -810,8 +832,10 @@ static void timer_intr_handler(unsigned long indx) return; } sqcp->in_use = 0; - if (sqcp->done_funct) + if (sqcp->done_funct) { + sqcp->a_cmnd->result = sqcp->scsi_result; sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */ + } sqcp->done_funct = NULL; spin_unlock_irqrestore(&queued_arr_lock, iflags); } @@ -1063,7 +1087,7 @@ static int schedule_resp(struct scsi_cmnd * cmnd, } if (cmnd && devip) { /* simulate autosense by this driver */ - if (CHECK_CONDITION == status_byte(scsi_result)) + if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff)) memcpy(cmnd->sense_buffer, devip->sense_buff, (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ? SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE); @@ -1099,6 +1123,8 @@ static int schedule_resp(struct scsi_cmnd * cmnd, sqcp->cmnd_timer.expires = jiffies + delta_jiff; add_timer(&sqcp->cmnd_timer); spin_unlock_irqrestore(&queued_arr_lock, iflags); + if (cmnd) + cmnd->result = 0; return 0; } } @@ -1163,7 +1189,7 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, if (1 != sscanf(arr, "%d", &pos)) return -EINVAL; scsi_debug_opts = pos; - if (SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) + if (scsi_debug_every_nth > 0) scsi_debug_cmnd_count = 0; return length; } @@ -1192,12 +1218,12 @@ static int scsi_debug_proc_info(char *buffer, char **start, off_t offset, return len; } -static ssize_t sdebug_delay_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_delay); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay); } -static ssize_t sdebug_delay_write(struct device_driver * ddp, +static ssize_t sdebug_delay_store(struct device_driver * ddp, const char * buf, size_t count) { int delay; @@ -1211,15 +1237,15 @@ static ssize_t sdebug_delay_write(struct device_driver * ddp, } return -EINVAL; } -DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_read, - sdebug_delay_write) +DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show, + sdebug_delay_store) -static ssize_t sdebug_opts_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "0x%x\n", scsi_debug_opts); + return snprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts); } -static ssize_t sdebug_opts_write(struct device_driver * ddp, +static ssize_t sdebug_opts_store(struct device_driver * ddp, const char * buf, size_t count) { int opts; @@ -1237,48 +1263,62 @@ static ssize_t sdebug_opts_write(struct device_driver * ddp, return -EINVAL; opts_done: scsi_debug_opts = opts; + scsi_debug_cmnd_count = 0; return count; } -DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_read, - sdebug_opts_write) +DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show, + sdebug_opts_store) -static ssize_t sdebug_num_devs_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_num_devs_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_num_devs); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_devs); } -DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_read, NULL) +DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_show, NULL) -static ssize_t sdebug_dev_size_mb_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_dev_size_mb); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb); } -DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_read, NULL) +DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL) -static ssize_t sdebug_every_nth_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_every_nth); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth); } -DRIVER_ATTR(every_nth, S_IRUGO, sdebug_every_nth_read, NULL) +static ssize_t sdebug_every_nth_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int nth; + + if ((count > 0) && (1 == sscanf(buf, "%d", &nth)) && (nth >= 0)) { + scsi_debug_every_nth = nth; + scsi_debug_cmnd_count = 0; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show, + sdebug_every_nth_store) -static ssize_t sdebug_max_luns_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_max_luns); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns); } -DRIVER_ATTR(max_luns, S_IRUGO, sdebug_max_luns_read, NULL) +DRIVER_ATTR(max_luns, S_IRUGO, sdebug_max_luns_show, NULL) -static ssize_t sdebug_scsi_level_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_scsi_level); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level); } -DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_read, NULL) +DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL) -static ssize_t sdebug_add_host_read(struct device_driver * ddp, char * buf) +static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf) { - return sprintf(buf, "%d\n", scsi_debug_add_host); + return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host); } -static ssize_t sdebug_add_host_write(struct device_driver * ddp, - const char * buf, size_t count) +static ssize_t sdebug_add_host_store(struct device_driver * ddp, + const char * buf, size_t count) { int delta_hosts, k; char work[20]; @@ -1322,8 +1362,8 @@ static ssize_t sdebug_add_host_write(struct device_driver * ddp, } return count; } -DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_read, - sdebug_add_host_write) +DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show, + sdebug_add_host_store) static void do_create_driverfs_files() { -- cgit v1.2.3 From 4955a5c85cf9fc5883e74915c199aa639770d891 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 14 Mar 2003 06:54:22 -0600 Subject: [PATCH] reduce stack in qlogicfc.c This is a start on reducing the stack usage in qlogicfc.c:: isp2x00_make_portdb(). I think that the stack reduction portion of it is fine, but I'm concerned about the function returning early due to kmalloc() failure, without making the port database. [reduces stack from 0xc38 to 0x34 bytes (P4 UP, gcc 2.96)] Can anyone suggest way(s) to have the isp2x00_make_portdb() function called over and over again until it gets its job done? Or does anyone even still use this driver? --- drivers/scsi/qlogicfc.c | 72 ++++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qlogicfc.c b/drivers/scsi/qlogicfc.c index 7b966c63bb9e..3cf04a66dc5d 100644 --- a/drivers/scsi/qlogicfc.c +++ b/drivers/scsi/qlogicfc.c @@ -836,14 +836,22 @@ static int isp2x00_make_portdb(struct Scsi_Host *host) short param[8]; int i, j; - struct id_name_map temp[QLOGICFC_MAX_ID + 1]; + struct id_name_map *map; /* base of array [QLOGICFC_MAX_ID + 1] */ + struct id_name_map *mapx; /* array entry pointer */ struct isp2x00_hostdata *hostdata; - isp2x00_disable_irqs(host); - - memset(temp, 0, sizeof(temp)); hostdata = (struct isp2x00_hostdata *) host->hostdata; + map = kmalloc((QLOGICFC_MAX_ID + 1) * sizeof(struct id_name_map), GFP_ATOMIC); + if (!map) { + printk("qlogicfc%d : error getting memory -- cannot make port database.\n", + hostdata->host_id); + goto fini; + } + memset(map, 0, (QLOGICFC_MAX_ID + 1) * sizeof(struct id_name_map)); + + isp2x00_disable_irqs(host); + #if ISP2x00_FABRIC for (i = 0x81; i < QLOGICFC_MAX_ID; i++) { param[0] = MBOX_PORT_LOGOUT; @@ -868,72 +876,74 @@ static int isp2x00_make_portdb(struct Scsi_Host *host) if (param[0] == MBOX_COMMAND_COMPLETE) { hostdata->port_id = ((u_int) param[3]) << 16; hostdata->port_id |= param[2]; - temp[0].loop_id = param[1]; - temp[0].wwn = hostdata->wwn; + map->loop_id = param[1]; + map->wwn = hostdata->wwn; } else { printk("qlogicfc%d : error getting scsi id.\n", hostdata->host_id); } - for (i = 0; i <=QLOGICFC_MAX_ID; i++) - temp[i].loop_id = temp[0].loop_id; + for (i = 0, mapx = map; i <= QLOGICFC_MAX_ID; i++, mapx++) + mapx->loop_id = map->loop_id; - for (i = 0, j = 1; i <= QLOGICFC_MAX_LOOP_ID; i++) { + for (i = 0, j = 1, mapx = map + 1; i <= QLOGICFC_MAX_LOOP_ID; i++) { param[0] = MBOX_GET_PORT_NAME; param[1] = (i << 8) & 0xff00; isp2x00_mbox_command(host, param); if (param[0] == MBOX_COMMAND_COMPLETE) { - temp[j].loop_id = i; - temp[j].wwn = ((u64) (param[2] & 0xff)) << 56; - temp[j].wwn |= ((u64) ((param[2] >> 8) & 0xff)) << 48; - temp[j].wwn |= ((u64) (param[3] & 0xff)) << 40; - temp[j].wwn |= ((u64) ((param[3] >> 8) & 0xff)) << 32; - temp[j].wwn |= ((u64) (param[6] & 0xff)) << 24; - temp[j].wwn |= ((u64) ((param[6] >> 8) & 0xff)) << 16; - temp[j].wwn |= ((u64) (param[7] & 0xff)) << 8; - temp[j].wwn |= ((u64) ((param[7] >> 8) & 0xff)); + mapx->loop_id = i; + mapx->wwn = ((u64) (param[2] & 0xff)) << 56; + mapx->wwn |= ((u64) ((param[2] >> 8) & 0xff)) << 48; + mapx->wwn |= ((u64) (param[3] & 0xff)) << 40; + mapx->wwn |= ((u64) ((param[3] >> 8) & 0xff)) << 32; + mapx->wwn |= ((u64) (param[6] & 0xff)) << 24; + mapx->wwn |= ((u64) ((param[6] >> 8) & 0xff)) << 16; + mapx->wwn |= ((u64) (param[7] & 0xff)) << 8; + mapx->wwn |= ((u64) ((param[7] >> 8) & 0xff)); j++; - + mapx++; } } #if ISP2x00_FABRIC - isp2x00_init_fabric(host, temp, j); + isp2x00_init_fabric(host, map, j); #endif - for (i = 0; i <= QLOGICFC_MAX_ID; i++) { - if (temp[i].wwn != hostdata->port_db[i].wwn) { - for (j = 0; j <= QLOGICFC_MAX_ID; j++) { - if (temp[j].wwn == hostdata->port_db[i].wwn) { - hostdata->port_db[i].loop_id = temp[j].loop_id; + for (i = 0, mapx = map; i <= QLOGICFC_MAX_ID; i++, mapx++) { + struct id_name_map *tmap; /* second array entry pointer */ + if (mapx->wwn != hostdata->port_db[i].wwn) { + for (j = 0, tmap = map; j <= QLOGICFC_MAX_ID; j++, tmap++) { + if (tmap->wwn == hostdata->port_db[i].wwn) { + hostdata->port_db[i].loop_id = tmap->loop_id; break; } } if (j == QLOGICFC_MAX_ID + 1) - hostdata->port_db[i].loop_id = temp[0].loop_id; + hostdata->port_db[i].loop_id = map->loop_id; for (j = 0; j <= QLOGICFC_MAX_ID; j++) { - if (hostdata->port_db[j].wwn == temp[i].wwn || !hostdata->port_db[j].wwn) { + if (hostdata->port_db[j].wwn == mapx->wwn || !hostdata->port_db[j].wwn) { break; } } if (j == QLOGICFC_MAX_ID + 1) printk("qlogicfc%d : Too many scsi devices, no more room in port map.\n", hostdata->host_id); if (!hostdata->port_db[j].wwn) { - hostdata->port_db[j].loop_id = temp[i].loop_id; - hostdata->port_db[j].wwn = temp[i].wwn; + hostdata->port_db[j].loop_id = mapx->loop_id; + hostdata->port_db[j].wwn = mapx->wwn; } } else - hostdata->port_db[i].loop_id = temp[i].loop_id; + hostdata->port_db[i].loop_id = mapx->loop_id; } isp2x00_enable_irqs(host); - + kfree(map); +fini: return 0; } -- cgit v1.2.3 From f9107051ff16ed0f7c2510e1189bc0255789571d Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Fri, 14 Mar 2003 06:56:34 -0600 Subject: [PATCH] sg version 3.5.28 for lk 2.5.64 Changelog: - remove hosts, host_strs and host_hdr from sg's procfs interface ** - add sysfs interface for allow_dio, def_reserved_size and version *** - switch boot time and module parameters to Rusty's moduleparam.h interface. This means, for example, the boot time "sg_def_reserved_size" parameter changes to "sg.def_reserved_size". ** Christoph moved the host listing functionality into a more central sysfs position (i.e. not dependent on sg). However scsi_debug is the only LLD that I can get to post any "host" info under the new arrangement. Should devices, device_strs and device_hdrs also be moved out of sg's procfs interface? *** I find sg's "debug" in its procfs interface very useful for debugging (sg itself amongst other things). However it does not seem suitable for sysfs. Should it move? --- drivers/scsi/sg.c | 150 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 98 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 91dace681ba1..bd6d50025126 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -18,10 +18,8 @@ * */ #include -#ifdef CONFIG_PROC_FS -static char *sg_version_str = "Version: 3.5.27 (20030130)"; -#endif -static int sg_version_num = 30527; /* 2 digits for each component */ +static char *sg_version_str = "3.5.28 [20030308]"; +static int sg_version_num = 30528; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First @@ -56,6 +54,7 @@ static int sg_version_num = 30527; /* 2 digits for each component */ #include #include #include +#include #include #include @@ -1327,27 +1326,6 @@ static struct file_operations sg_fops = { .fasync = sg_fasync, }; -#ifndef MODULE -static int __init -sg_def_reserved_size_setup(char *str) -{ - int tmp; - - if (get_option(&str, &tmp) == 1) { - def_reserved_size = tmp; - if (tmp >= 0) - sg_big_buff = tmp; - return 1; - } else { - printk(KERN_WARNING "sg_def_reserved_size : usage " - "sg_def_reserved_size=n (n could be 65536, 131072 or 262144)\n"); - return 0; - } -} - -__setup("sg_def_reserved_size=", sg_def_reserved_size_setup); -#endif - /* Driverfs file support */ static ssize_t sg_device_kdev_read(struct device *driverfs_dev, char *page) @@ -1564,16 +1542,77 @@ sg_detach(Scsi_Device * scsidp) scsi_sleep(2); /* dirty detach so delay device destruction */ } +/* Set 'perm' (4th argument) to 0 to disable module_param's definition + * of sysfs parameters (which module_param doesn't yet support). + * Sysfs parameters defined explicitly below. + */ +module_param_named(def_reserved_size, def_reserved_size, int, 0); +module_param_named(allow_dio, sg_allow_dio, int, 0); + MODULE_AUTHOR("Douglas Gilbert"); MODULE_DESCRIPTION("SCSI generic (sg) driver"); - -#ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); -#endif -MODULE_PARM(def_reserved_size, "i"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); +static ssize_t sg_allow_dio_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sg_allow_dio); +} +static ssize_t sg_allow_dio_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + if (1 == sscanf(buf, "%d", &sg_allow_dio)) { + sg_allow_dio = sg_allow_dio ? 1 : 0; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(allow_dio, S_IRUGO | S_IWUSR, sg_allow_dio_show, + sg_allow_dio_store) + +static ssize_t sg_def_reserved_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", sg_big_buff); +} +static ssize_t sg_def_reserved_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + if (1 == sscanf(buf, "%d", &def_reserved_size)) { + if (def_reserved_size >= 0) { + sg_big_buff = def_reserved_size; + return count; + } + } + return -EINVAL; +} +DRIVER_ATTR(def_reserved_size, S_IRUGO | S_IWUSR, sg_def_reserved_show, + sg_def_reserved_store) + +static ssize_t sg_version_show(struct device_driver * ddp, char * buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", sg_version_str); +} +DRIVER_ATTR(version, S_IRUGO, sg_version_show, NULL) + +static void do_create_driverfs_files(void) +{ + struct device_driver * driverfs = &sg_template.scsi_driverfs_driver; + + driver_create_file(driverfs, &driver_attr_allow_dio); + driver_create_file(driverfs, &driver_attr_def_reserved_size); + driver_create_file(driverfs, &driver_attr_version); +} + +static void do_remove_driverfs_files(void) +{ + struct device_driver * driverfs = &sg_template.scsi_driverfs_driver; + + driver_remove_file(driverfs, &driver_attr_version); + driver_remove_file(driverfs, &driver_attr_def_reserved_size); + driver_remove_file(driverfs, &driver_attr_allow_dio); +} + static int __init init_sg(void) { @@ -1591,12 +1630,14 @@ init_sg(void) #ifdef CONFIG_PROC_FS sg_proc_init(); #endif /* CONFIG_PROC_FS */ + do_create_driverfs_files(); return 0; } static void __exit exit_sg(void) { + do_remove_driverfs_files(); #ifdef CONFIG_PROC_FS sg_proc_cleanup(); #endif /* CONFIG_PROC_FS */ @@ -2656,10 +2697,6 @@ sg_get_dev(int dev) static struct proc_dir_entry *sg_proc_sgp = NULL; static char sg_proc_sg_dirname[] = "sg"; -static const char *sg_proc_leaf_names[] = { "allow_dio", "def_reserved_size", - "debug", "devices", "device_hdr", "device_strs", - "hosts", "host_hdr", "host_strs", "version" -}; static int sg_proc_adio_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data); @@ -2693,13 +2730,21 @@ static int sg_proc_version_read(char *buffer, char **start, off_t offset, int size, int *eof, void *data); static int sg_proc_version_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static read_proc_t *sg_proc_leaf_reads[] = { - sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, - sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, - sg_proc_version_read + +struct sg_proc_leaf { + const char * name; + read_proc_t * rf; + write_proc_t * wf; }; -static write_proc_t *sg_proc_leaf_writes[] = { - sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0 + +static struct sg_proc_leaf sg_proc_leaf_arr[] = { + {"allow_dio", sg_proc_adio_read, sg_proc_adio_write}, + {"def_reserved_size", sg_proc_dressz_read, sg_proc_dressz_write}, + {"debug", sg_proc_debug_read, NULL}, + {"devices", sg_proc_dev_read, NULL}, + {"device_hdr", sg_proc_devhdr_read, NULL}, + {"device_strs", sg_proc_devstrs_read, NULL}, + {"version", sg_proc_version_read, NULL} }; #define PRINT_PROC(fmt,args...) \ @@ -2729,9 +2774,10 @@ static int sg_proc_init() { int k, mask; - int leaves = - sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); + int num_leaves = + sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]); struct proc_dir_entry *pdep; + struct sg_proc_leaf * leaf; if (!proc_scsi) return 1; @@ -2739,14 +2785,14 @@ sg_proc_init() S_IFDIR | S_IRUGO | S_IXUGO, proc_scsi); if (!sg_proc_sgp) return 1; - for (k = 0; k < leaves; ++k) { - mask = sg_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR : S_IRUGO; - pdep = - create_proc_entry(sg_proc_leaf_names[k], mask, sg_proc_sgp); + for (k = 0; k < num_leaves; ++k) { + leaf = &sg_proc_leaf_arr[k]; + mask = leaf->wf ? S_IRUGO | S_IWUSR : S_IRUGO; + pdep = create_proc_entry(leaf->name, mask, sg_proc_sgp); if (pdep) { - pdep->read_proc = sg_proc_leaf_reads[k]; - if (sg_proc_leaf_writes[k]) - pdep->write_proc = sg_proc_leaf_writes[k]; + pdep->read_proc = leaf->rf; + if (leaf->wf) + pdep->write_proc = leaf->wf; } } return 0; @@ -2756,13 +2802,13 @@ static void sg_proc_cleanup() { int k; - int leaves = - sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); + int num_leaves = + sizeof (sg_proc_leaf_arr) / sizeof (sg_proc_leaf_arr[0]); if ((!proc_scsi) || (!sg_proc_sgp)) return; - for (k = 0; k < leaves; ++k) - remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp); + for (k = 0; k < num_leaves; ++k) + remove_proc_entry(sg_proc_leaf_arr[k].name, sg_proc_sgp); remove_proc_entry(sg_proc_sg_dirname, proc_scsi); } -- cgit v1.2.3 From c4ab9d7892e7f3bf3698f514c8db8f5e719f2b66 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Mar 2003 10:37:42 +0000 Subject: [TTY] Register tty devclass before use. Register the tty devclass with sysfs before tty drivers initialise - sysfs requires structures to be registered before use. This is required for the previous serial csets, as well as any drivers which are initialising using __initcall() or module_init(). --- drivers/char/tty_io.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 32488d71037f..86e3216d0547 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2235,14 +2235,19 @@ struct device_class tty_devclass = { }; EXPORT_SYMBOL(tty_devclass); +static int __init tty_devclass_init(void) +{ + return devclass_register(&tty_devclass); +} + +postcore_initcall(tty_devclass_init); + /* * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ void __init tty_init(void) { - devclass_register(&tty_devclass); - /* * dev_tty_driver and dev_console_driver are actually magic * devices which get redirected at open time. Nevertheless, -- cgit v1.2.3 From b4997f76de12ec4757c076ef5f62447d8d2e1dac Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Sat, 15 Mar 2003 21:05:30 -0600 Subject: scsi_debug version 1.68 mark III Changelog since version 1.68 mark II: - merge Mike Anderson's probe() cleanup - num_devs is now "per host" - num_devs is sysfs writeable - add slave_alloc skeleton code So to simulate 154 disks (for example) one might use: # modprobe scsi_debug add_host=11 num_devs=14 With max_luns at its default value of 2, 14 is the maximum number of devices per host scsi_debug will respond to (i.e. 7 targets, each with 2 lus). Documentation updated at: http://www.torque.net/sg/sdebug25.html --- drivers/scsi/scsi_debug.c | 489 +++++++++++++++++++++++++++------------------- drivers/scsi/scsi_debug.h | 4 +- 2 files changed, 294 insertions(+), 199 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index fdc13f3d70d5..eed0e0583619 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -11,7 +11,7 @@ * (or disk like devices) sharing a common amount of RAM * * - * For documentation see http://www.torque.net/sg/sdebug.html + * For documentation see http://www.torque.net/sg/sdebug25.html * * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] * dpg: work for devfs large number of disks [20010809] @@ -54,14 +54,7 @@ #include "scsi_debug.h" -static const char * scsi_debug_version_str = "Version: 1.68 (20030309)"; - -#ifndef SCSI_CMD_READ_16 -#define SCSI_CMD_READ_16 0x88 -#endif -#ifndef SCSI_CMD_WRITE_16 -#define SCSI_CMD_WRITE_16 0x8a -#endif +static const char * scsi_debug_version_str = "Version: 1.68 (20030314)"; #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */ @@ -75,8 +68,6 @@ static const char * scsi_debug_version_str = "Version: 1.68 (20030309)"; #define DEF_NUM_HOST 1 #define DEF_OPTS 0 -#define MAX_NUM_HOSTS 128 - /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_MEDIUM_ERR 2 @@ -93,7 +84,7 @@ static const char * scsi_debug_version_str = "Version: 1.68 (20030309)"; #define OPT_MEDIUM_ERR_ADDR 0x1234 /* that's sector 4660 in decimal */ static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; -static int scsi_debug_num_devs = DEF_NUM_DEVS; +static int scsi_debug_num_devs = DEF_NUM_DEVS; /* max devs per host */ static int scsi_debug_opts = DEF_OPTS; static int scsi_debug_every_nth = DEF_EVERY_NTH; static int scsi_debug_cmnd_count = 0; @@ -120,25 +111,28 @@ static int sdebug_sectors_per; /* sectors per cylinder */ #define SECT_SIZE (1 << POW2_SECT_SIZE) #define SECT_SIZE_PER(TGT) SECT_SIZE -struct sdebug_host_info { - struct Scsi_Host *shost; - struct device *dev; -}; - -struct sdebug_host_info * scsi_debug_hosts; - #define SDEBUG_SENSE_LEN 32 struct sdebug_dev_info { + struct list_head dev_list; unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */ unsigned int channel; unsigned int target; unsigned int lun; - struct Scsi_Host *host; + struct sdebug_host_info *sdbg_host; char reset; char used; }; -static struct sdebug_dev_info * devInfop; + +struct sdebug_host_info { + struct list_head host_list; + struct Scsi_Host *shost; + struct device *dev; + struct list_head dev_info_list; +}; + +static LIST_HEAD(sdebug_host_list); +static spinlock_t sdebug_host_list_lock = SPIN_LOCK_UNLOCKED; typedef void (* done_funct_t) (struct scsi_cmnd *); @@ -164,9 +158,15 @@ static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED; static rwlock_t atomic_rw = RW_LOCK_UNLOCKED; static char sdebug_proc_name[] = "scsi_debug"; + +static int sdebug_driver_probe(struct device *); +static int sdebug_driver_remove(struct device *); + static struct device_driver sdebug_driverfs_driver = { - .name = sdebug_proc_name, - .devclass = &shost_devclass, + .name = sdebug_proc_name, + .probe = sdebug_driver_probe, + .remove = sdebug_driver_remove, + .devclass = &shost_devclass, }; static const int check_condition_result = @@ -185,7 +185,7 @@ static int resp_write(struct scsi_cmnd * SCpnt, int upper_blk, int block, static int resp_report_luns(unsigned char * cmd, unsigned char * buff, int bufflen, struct sdebug_dev_info * devip); static void timer_intr_handler(unsigned long); -static struct sdebug_dev_info * devInfoReg(struct scsi_cmnd *scmd); +static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev); static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, int asc, int asq, int inbandLen); static int check_reset(struct scsi_cmnd * SCpnt, @@ -200,15 +200,29 @@ static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, const char * dev_id_str, int dev_id_str_len); static void do_create_driverfs_files(void); static void do_remove_driverfs_files(void); -static void sdebug_add_shost(int num); -static void sdebug_remove_shost(int num); -static int sdebug_add_adapter(int num); -static void sdebug_remove_adapter(int num); +static int sdebug_add_adapter(void); +static void sdebug_remove_adapter(void); static struct device pseudo_primary; static struct bus_type pseudo_lld_bus; -int scsi_debug_register_driver(struct device_driver *); -int scsi_debug_unregister_driver(struct device_driver *); +static int scsi_debug_register_driver(struct device_driver *); +static int scsi_debug_unregister_driver(struct device_driver *); + +static struct sdebug_host_info * + sdebug_shost_to_host_info(struct Scsi_Host *shost) +{ + struct sdebug_host_info * sdbg_host, * found = NULL; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + if (sdbg_host->shost == shost) { + found = sdbg_host; + break; + } + } + spin_unlock(&sdebug_host_list_lock); + return found; +} static unsigned char * scatg2virt(const struct scatterlist * sclp) { @@ -262,7 +276,7 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) if (SCpnt->device->lun >= scsi_debug_max_luns) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); - devip = devInfoReg(SCpnt); + devip = devInfoReg(SCpnt->device); if (NULL == devip) return schedule_resp(SCpnt, NULL, done, DID_NO_CONNECT << 16, 0); @@ -340,14 +354,14 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) buff[7] = SECT_SIZE_PER(target) & 0xff; } break; - case SCSI_CMD_READ_16: /* SBC-2 */ + case READ_16: case READ_12: case READ_10: case READ_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; - if ((*cmd) == SCSI_CMD_READ_16) { + if ((*cmd) == READ_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + @@ -377,14 +391,14 @@ int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done) case REPORT_LUNS: errsts = resp_report_luns(cmd, buff, bufflen, devip); break; - case SCSI_CMD_WRITE_16: /* SBC-2 */ + case WRITE_16: case WRITE_12: case WRITE_10: case WRITE_6: if ((errsts = check_reset(SCpnt, devip))) break; upper_blk = 0; - if ((*cmd) == SCSI_CMD_WRITE_16) { + if ((*cmd) == WRITE_16) { upper_blk = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); block = cmd[9] + (cmd[8] << 8) + @@ -508,7 +522,7 @@ static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, int dev_id_num, len; char dev_id_str[6]; - dev_id_num = ((devip->host->host_no + 1) * 2000) + + dev_id_num = ((devip->sdbg_host->shost->host_no + 1) * 2000) + (devip->target * 1000) + devip->lun; len = snprintf(dev_id_str, 6, "%d", dev_id_num); len = (len > 6) ? 6 : len; @@ -840,9 +854,16 @@ static void timer_intr_handler(unsigned long indx) spin_unlock_irqrestore(&queued_arr_lock, iflags); } +static int scsi_debug_slave_alloc(struct scsi_device * sdp) +{ + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n", + sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); + return 0; +} + static int scsi_debug_slave_configure(struct scsi_device * sdp) { - int k; struct sdebug_dev_info * devip; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) @@ -850,16 +871,8 @@ static int scsi_debug_slave_configure(struct scsi_device * sdp) sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if ((devip->channel == sdp->channel) && - (devip->target == sdp->id) && - (devip->lun == sdp->lun) && - (devip->host == sdp->host)) { - sdp->hostdata = devip; - break; - } - } + devip = devInfoReg(sdp); + sdp->hostdata = devip; if (sdp->host->cmd_per_lun) scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING, sdp->host->cmd_per_lun); @@ -876,43 +889,47 @@ static void scsi_debug_slave_destroy(struct scsi_device * sdp) sdp->host->host_no, sdp->channel, sdp->id, sdp->lun); if (devip) { /* make this slot avaliable for re-use */ - memset(devip, 0, sizeof(struct sdebug_dev_info)); + devip->used = 0; sdp->hostdata = NULL; } } -static struct sdebug_dev_info * devInfoReg(struct scsi_cmnd *scmd) +static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev) { - int k; - struct scsi_device * sdp = scmd->device; + struct sdebug_host_info * sdbg_host; + struct sdebug_dev_info * open_devip = NULL; struct sdebug_dev_info * devip = - (struct sdebug_dev_info *)sdp->hostdata; + (struct sdebug_dev_info *)sdev->hostdata; if (devip) return devip; - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if ((devip->channel == scmd->device->channel) && - (devip->target == scmd->device->id) && - (devip->lun == scmd->device->lun) && - (devip->host == scmd->device->host)) - return devip; - } - for (k = 0; k < scsi_debug_num_devs; ++k) { - devip = &devInfop[k]; - if (!devip->used) { - devip->channel = scmd->device->channel; - devip->target = scmd->device->id; - devip->lun = scmd->device->lun; - devip->host = scmd->device->host; - devip->reset = 1; - devip->used = 1; - memset(devip->sense_buff, 0, SDEBUG_SENSE_LEN); - devip->sense_buff[0] = 0x70; - return devip; + sdbg_host = sdebug_shost_to_host_info(sdev->host); + if(! sdbg_host) { + printk(KERN_ERR "Unable to locate host info\n"); + return NULL; + } + list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) { + if ((devip->used) && (devip->channel == sdev->channel) && + (devip->target == sdev->id) && + (devip->lun == sdev->lun)) + return devip; + else { + if ((!devip->used) && (!open_devip)) + open_devip = devip; } } - return NULL; + if (open_devip) { + open_devip->channel = sdev->channel; + open_devip->target = sdev->id; + open_devip->lun = sdev->lun; + open_devip->sdbg_host = sdbg_host; + open_devip->reset = 1; + open_devip->used = 1; + memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN); + open_devip->sense_buff[0] = 0x70; + return open_devip; + } + return NULL; } static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, @@ -970,7 +987,7 @@ static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) printk(KERN_INFO "scsi_debug: device_reset\n"); ++num_dev_resets; if (SCpnt) { - devip = devInfoReg(SCpnt); + devip = devInfoReg(SCpnt->device); if (devip) devip->reset = 1; } @@ -979,17 +996,21 @@ static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt) static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) { - struct scsi_device * sdp; - struct Scsi_Host * hp; - int k; + struct sdebug_host_info *sdbg_host; + struct sdebug_dev_info * dev_info; + struct scsi_device * sdp; + struct Scsi_Host * hp; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: bus_reset\n"); ++num_bus_resets; - if (SCpnt && ((sdp = SCpnt->device)) && ((hp = SCpnt->device->host))) { - for (k = 0; k < scsi_debug_num_devs; ++k) { - if (hp == devInfop[k].host) - devInfop[k].reset = 1; + if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) { + sdbg_host = sdebug_shost_to_host_info(hp); + if (sdbg_host) { + list_for_each_entry(dev_info, + &sdbg_host->dev_info_list, + dev_list) + dev_info->reset = 1; } } return SUCCESS; @@ -997,15 +1018,20 @@ static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt) static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt) { - int k; + struct sdebug_host_info * sdbg_host; + struct sdebug_dev_info * dev_info; if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: host_reset\n"); ++num_host_resets; - for (k = 0; k < scsi_debug_num_devs; ++k) - devInfop[k].reset = 1; + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + list_for_each_entry(dev_info, &sdbg_host->dev_info_list, + dev_list) + dev_info->reset = 1; + } + spin_unlock(&sdebug_host_list_lock); stop_all_queued(); - return SUCCESS; } @@ -1146,7 +1172,7 @@ MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); MODULE_LICENSE("GPL"); -MODULE_PARM_DESC(num_devs, "number of SCSI devices to simulate"); +MODULE_PARM_DESC(num_devs, "number of SCSI devices per host to simulate"); MODULE_PARM_DESC(max_luns, "number of SCSI LUNs per target to simulate"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate"); MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs"); @@ -1273,7 +1299,19 @@ static ssize_t sdebug_num_devs_show(struct device_driver * ddp, char * buf) { return snprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_devs); } -DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_show, NULL) +static ssize_t sdebug_num_devs_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) { + scsi_debug_num_devs = n; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(num_devs, S_IRUGO | S_IWUSR, sdebug_num_devs_show, + sdebug_num_devs_store) static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf) { @@ -1320,7 +1358,7 @@ static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf) static ssize_t sdebug_add_host_store(struct device_driver * ddp, const char * buf, size_t count) { - int delta_hosts, k; + int delta_hosts; char work[20]; if (1 != sscanf(buf, "%10s", work)) @@ -1337,27 +1375,11 @@ static ssize_t sdebug_add_host_store(struct device_driver * ddp, } if (delta_hosts > 0) { do { - for (k = 0; k < MAX_NUM_HOSTS; ++k) { - if (NULL == scsi_debug_hosts[k].shost) { - sdebug_add_shost(k); - break; - } - } - if (k == MAX_NUM_HOSTS) - break; - ++scsi_debug_add_host; + sdebug_add_adapter(); } while (--delta_hosts); } else if (delta_hosts < 0) { do { - for (k = MAX_NUM_HOSTS - 1; k >= 0; --k) { - if (scsi_debug_hosts[k].shost) { - sdebug_remove_shost(k); - break; - } - } - if (k < 0) - break; - --scsi_debug_add_host; + sdebug_remove_adapter(); } while (++delta_hosts); } return count; @@ -1389,44 +1411,10 @@ static void do_remove_driverfs_files() driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay); } -static void sdebug_add_shost(int num) -{ - struct Scsi_Host * hpnt; - int err; - - if (sdebug_add_adapter(num)){ - printk(KERN_ERR "sdebug_add_shost: sdebug_add_adapter failed\n"); - return; - } - hpnt = scsi_register(&sdebug_driver_template, 0); - if (NULL == hpnt) { - sdebug_remove_adapter(num); - printk(KERN_ERR "sdebug_add_shost: scsi_register failed\n"); - return; - } - err = scsi_add_host(hpnt, scsi_debug_hosts[num].dev); - if (err) { - printk(KERN_ERR "sdebug_add_shost: scsi_add_host failed\n"); - scsi_unregister(hpnt); - sdebug_remove_adapter(num); - return; - } - hpnt->max_lun = scsi_debug_max_luns; - - scsi_debug_hosts[num].shost = hpnt; -} - -static void sdebug_remove_shost(int num) -{ - scsi_remove_host(scsi_debug_hosts[num].shost); - scsi_unregister(scsi_debug_hosts[num].shost); - sdebug_remove_adapter(num); - scsi_debug_hosts[num].shost = NULL; -} - static int __init scsi_debug_init(void) { unsigned long sz; + int host_to_add; int k; sdebug_store_size = (unsigned long)scsi_debug_dev_size_mb * 1048576; @@ -1449,30 +1437,10 @@ static int __init scsi_debug_init(void) (sdebug_sectors_per * sdebug_heads); } - if (scsi_debug_num_devs > 0) { - sz = sizeof(struct sdebug_dev_info) * scsi_debug_num_devs; - devInfop = vmalloc(sz); - if (NULL == devInfop) { - printk(KERN_ERR "scsi_debug_init: out of memory\n"); - return -ENOMEM; - } - memset(devInfop, 0, sz); - } - - sz = sizeof(struct sdebug_host_info) * MAX_NUM_HOSTS; - scsi_debug_hosts = vmalloc(sz); - if (NULL == scsi_debug_hosts) { - printk(KERN_ERR "scsi_debug_init: out of memory 1\n"); - return -ENOMEM; - } - memset(scsi_debug_hosts, 0, sz); - sz = sdebug_store_size; fake_storep = vmalloc(sz); if (NULL == fake_storep) { printk(KERN_ERR "scsi_debug_init: out of memory, 1\n"); - if (devInfop) - vfree(devInfop); return -ENOMEM; } memset(fake_storep, 0, sz); @@ -1486,15 +1454,16 @@ static int __init scsi_debug_init(void) sdebug_driver_template.proc_name = (char *)sdebug_proc_name; - for (k = 0; (k < scsi_debug_add_host) && (k < MAX_NUM_HOSTS); k++) { - sdebug_add_shost(k); - if (NULL == scsi_debug_hosts[k].shost) { - printk(KERN_ERR "scsi_debug_init: " - "sdebug_add_shost failed k=%d\n", k); - break; - } - } - scsi_debug_add_host = k; // number of hosts actually present + host_to_add = scsi_debug_add_host; + scsi_debug_add_host = 0; + + for (k = 0; k < host_to_add; k++) { + if (sdebug_add_adapter()) { + printk(KERN_ERR "scsi_debug_init: " + "sdebug_add_adapter failed k=%d\n", k); + break; + } + } if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { printk(KERN_INFO "scsi_debug: ... built %d host(s)\n", @@ -1505,13 +1474,7 @@ static int __init scsi_debug_init(void) static void __exit scsi_debug_exit(void) { - int k; - - for (k = MAX_NUM_HOSTS - 1; k >= 0; --k) { - if (scsi_debug_hosts[k].shost) { - sdebug_remove_shost(k); - } - } + /* free up adapters here ?? */ stop_all_queued(); do_remove_driverfs_files(); scsi_debug_unregister_driver(&sdebug_driverfs_driver); @@ -1519,8 +1482,6 @@ static void __exit scsi_debug_exit(void) device_unregister(&pseudo_primary); vfree(fake_storep); - if (devInfop) - vfree(devInfop); } device_initcall(scsi_debug_init); @@ -1542,7 +1503,7 @@ static struct bus_type pseudo_lld_bus = { .match = pseudo_lld_bus_match, }; -int scsi_debug_register_driver(struct device_driver *dev_driver) +static int scsi_debug_register_driver(struct device_driver *dev_driver) { dev_driver->bus = &pseudo_lld_bus; driver_register(dev_driver); @@ -1550,36 +1511,168 @@ int scsi_debug_register_driver(struct device_driver *dev_driver) return 0; } -int scsi_debug_unregister_driver(struct device_driver *dev_driver) +static int scsi_debug_unregister_driver(struct device_driver *dev_driver) { driver_unregister(dev_driver); return 0; } -static int sdebug_add_adapter(int num) +static void sdebug_release_adapter(struct device * dev) { - struct device * dev; + kfree(dev); +} - dev = kmalloc(sizeof(*dev),GFP_KERNEL); - if (NULL == dev) { - printk(KERN_ERR "%s: out of memory\n", __FUNCTION__); - return 1; - } +static int sdebug_add_adapter() +{ + struct device * dev; + int error; + + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + return -ENOMEM; + } + memset(dev, 0, sizeof(*dev)); - memset(dev, 0, sizeof(*dev)); - dev->bus = &pseudo_lld_bus; - dev->parent = &pseudo_primary; - sprintf(dev->name, "scsi debug adapter"); - sprintf(dev->bus_id, "adapter%d", num); + dev->bus = &pseudo_lld_bus; + dev->parent = &pseudo_primary; + dev->release = &sdebug_release_adapter; + sprintf(dev->name, "scsi debug adapter"); + sprintf(dev->bus_id, "adapter%d", scsi_debug_add_host); - device_register(dev); + error = device_register(dev); - scsi_debug_hosts[num].dev = dev; + if (error) + kfree(dev); + else + ++scsi_debug_add_host; - return 0; + return error; +} + +static void sdebug_remove_adapter() +{ + struct sdebug_host_info * sdbg_host = NULL; + + spin_lock(&sdebug_host_list_lock); + if (!list_empty(&sdebug_host_list)) + sdbg_host = list_entry(sdebug_host_list.prev, + struct sdebug_host_info, host_list); + spin_unlock(&sdebug_host_list_lock); + + device_unregister(sdbg_host->dev); + --scsi_debug_add_host; } -static void sdebug_remove_adapter(int num) +static int sdebug_driver_probe(struct device * dev) { - device_unregister(scsi_debug_hosts[num].dev); + int k; + int error = 0; + struct sdebug_host_info *sdbg_host; + struct sdebug_dev_info *sdbg_devinfo; + struct list_head *lh, *lh_sf; + struct Scsi_Host *hpnt; + + sdbg_host = kmalloc(sizeof(*sdbg_host),GFP_KERNEL); + if (NULL == sdbg_host) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + return -ENOMEM; + } + memset(sdbg_host, 0, sizeof(*sdbg_host)); + + INIT_LIST_HEAD(&sdbg_host->dev_info_list); + + for (k = 0; k < scsi_debug_num_devs; k++) { + sdbg_devinfo = kmalloc(sizeof(*sdbg_devinfo),GFP_KERNEL); + if (NULL == sdbg_devinfo) { + printk(KERN_ERR "%s: out of memory at line %d\n", + __FUNCTION__, __LINE__); + error = -ENOMEM; + } + memset(sdbg_devinfo, 0, sizeof(*sdbg_devinfo)); + sdbg_devinfo->sdbg_host = sdbg_host; + list_add_tail(&sdbg_devinfo->dev_list, + &sdbg_host->dev_info_list); + } + + list_add_tail(&sdbg_host->host_list, &sdebug_host_list); + + hpnt = scsi_register(&sdebug_driver_template, 0); + if (NULL == hpnt) { + printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__); + error = -ENODEV; + goto clean1; + } + + sdbg_host->shost = hpnt; + sdbg_host->dev = dev; + hpnt->max_lun = scsi_debug_max_luns; + + error = scsi_add_host(hpnt, sdbg_host->dev); + if (error) { + printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__); + error = -ENODEV; + goto clean2; + } + + + return 0; + +clean2: + scsi_unregister(hpnt); +clean1: + list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) { + sdbg_devinfo = list_entry(lh, struct sdebug_dev_info, + dev_list); + list_del(&sdbg_devinfo->dev_list); + kfree(sdbg_devinfo); + } + + kfree(sdbg_host); + + return error; +} + +static int sdebug_driver_remove(struct device * dev) +{ + struct list_head *lh, *lh_sf; + struct sdebug_dev_info *sdbg_devinfo; + struct sdebug_host_info *sdbg_host, *found = NULL; + + spin_lock(&sdebug_host_list_lock); + list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) { + if (sdbg_host->dev == dev) { + list_del(&sdbg_host->host_list); + found = sdbg_host; + break; + } + } + spin_unlock(&sdebug_host_list_lock); + + if (!found) { + printk(KERN_ERR "%s: sdebug_host_info not found\n", + __FUNCTION__); + return -ENODEV; + } + + + if (scsi_remove_host(sdbg_host->shost)) { + printk(KERN_ERR "%s: scsi_remove_host failed\n", __FUNCTION__); + return -EBUSY; + } + + scsi_unregister(sdbg_host->shost); + + list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) { + sdbg_devinfo = list_entry(lh, struct sdebug_dev_info, + dev_list); + list_del(&sdbg_devinfo->dev_list); + kfree(sdbg_devinfo); + } + + kfree(sdbg_host); + + return 0; } diff --git a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h index 36d7bbebc9cd..2d9ab66993e2 100644 --- a/drivers/scsi/scsi_debug.h +++ b/drivers/scsi/scsi_debug.h @@ -2,6 +2,7 @@ #include +static int scsi_debug_slave_alloc(struct scsi_device *); static int scsi_debug_slave_configure(struct scsi_device *); static void scsi_debug_slave_destroy(struct scsi_device *); static int scsi_debug_queuecommand(struct scsi_cmnd *, @@ -27,8 +28,9 @@ static Scsi_Host_Template sdebug_driver_template = { .proc_info = scsi_debug_proc_info, .name = "SCSI DEBUG", .info = scsi_debug_info, + .slave_alloc = scsi_debug_slave_alloc, .slave_configure = scsi_debug_slave_configure, - .slave_destroy = scsi_debug_slave_destroy, + .slave_destroy = scsi_debug_slave_destroy, .ioctl = scsi_debug_ioctl, .queuecommand = scsi_debug_queuecommand, .eh_abort_handler = scsi_debug_abort, -- cgit v1.2.3 From a9d96a6e1f57deea016cdf580d1fdee2ecef6df8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 21:33:30 +0000 Subject: [PCI] pci-6 - Fix scanning of non-zero functions Fix breakage in pci-3 - we scanned all functions if function 0 was not present. This causes some host bridges to lock up when scanning devfn 255 on PPC machines. --- drivers/pci/probe.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 1c66d319f420..2715583870db 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -520,11 +520,14 @@ int __devinit pci_scan_slot(struct pci_bus *bus, int devfn) struct pci_dev *dev; dev = pci_scan_device(bus, devfn); - if (!dev) - continue; - - if (func != 0) + if (func == 0) { + if (!dev) + break; + } else { + if (!dev) + continue; dev->multifunction = 1; + } /* Fix up broken headers */ pci_fixup_device(PCI_FIXUP_HEADER, dev); -- cgit v1.2.3 From 032d6c6ef2b24013633f987cdf2d8fb88eadc202 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 21:42:43 +0000 Subject: [PCI] pci-7: Remove second argument to pcibios_update_resource() Patch from Ivan Kokshaysky remove the "parent" or "root" second argument to pcibios_update_resource(). This highlights the following architectures doing something wrong in their implementation: - ia64 - mips it8172 - mips "generic mips boards" - mips64 "generic mips boards" - mips64 IP27 --- arch/alpha/kernel/pci.c | 4 ++-- arch/arm/kernel/bios32.c | 4 ++-- arch/i386/pci/i386.c | 4 ++-- arch/ia64/pci/pci.c | 7 ++++--- arch/mips/ddb5074/pci.c | 4 ++-- arch/mips/ddb5476/pci.c | 4 ++-- arch/mips/ddb5xxx/common/pci.c | 4 ++-- arch/mips/gt64120/common/pci.c | 4 ++-- arch/mips/ite-boards/generic/it8172_pci.c | 7 ++++--- arch/mips/kernel/pci.c | 4 ++-- arch/mips/mips-boards/generic/pci.c | 7 ++++--- arch/mips/sni/pci.c | 4 ++-- arch/mips64/mips-boards/generic/pci.c | 7 ++++--- arch/mips64/sgi-ip27/ip27-pci.c | 7 ++++--- arch/mips64/sgi-ip32/ip32-pci.c | 4 ++-- arch/parisc/kernel/pci.c | 1 - arch/ppc/kernel/pci.c | 4 ++-- arch/ppc64/kernel/pci.c | 4 ++-- arch/sh/kernel/pcibios.c | 4 ++-- arch/sparc/kernel/pcic.c | 4 ++-- arch/sparc64/kernel/pci.c | 4 ++-- arch/v850/kernel/rte_mb_a_pci.c | 3 +-- arch/x86_64/pci/x86-64.c | 4 ++-- drivers/pci/setup-res.c | 4 ++-- include/linux/pci.h | 3 +-- 25 files changed, 56 insertions(+), 54 deletions(-) (limited to 'drivers') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 3b99a1d12878..81e079568688 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -265,8 +265,8 @@ pcibios_fixup_bus(struct pci_bus *bus) } void -pcibios_update_resource(struct pci_dev *dev, struct resource *parent, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { struct pci_controller *hose = dev->sysdata; struct resource *root; diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 0498981652f5..8df79c2cdee3 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -260,8 +260,8 @@ struct pci_fixup pcibios_fixups[] = { }; void __devinit -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { struct pci_sys_data *sys = dev->sysdata; u32 val, check; diff --git a/arch/i386/pci/i386.c b/arch/i386/pci/i386.c index 9cfc595855de..36e5d4e30bbc 100644 --- a/arch/i386/pci/i386.c +++ b/arch/i386/pci/i386.c @@ -34,8 +34,8 @@ #include "pci.h" void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index c0e7bac3b123..84e49ffc52b1 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -144,8 +144,8 @@ pcibios_fixup_bus (struct pci_bus *b) } void __devinit -pcibios_update_resource (struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource (struct pci_dev *dev, struct resource *res, + int resource) { unsigned long where, size; u32 reg; @@ -153,7 +153,8 @@ pcibios_update_resource (struct pci_dev *dev, struct resource *root, where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + /* FIXME - this doesn't work for PCI-PCI bridges. */ + reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); pci_write_config_dword(dev, where, reg); /* ??? FIXME -- record old value for shutdown. */ diff --git a/arch/mips/ddb5074/pci.c b/arch/mips/ddb5074/pci.c index 57a0b09ea24f..b97df65f4f3f 100644 --- a/arch/mips/ddb5074/pci.c +++ b/arch/mips/ddb5074/pci.c @@ -341,8 +341,8 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +void pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/mips/ddb5476/pci.c b/arch/mips/ddb5476/pci.c index d8f2c6d57f0d..473aa79e4d65 100644 --- a/arch/mips/ddb5476/pci.c +++ b/arch/mips/ddb5476/pci.c @@ -399,8 +399,8 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +void pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/mips/ddb5xxx/common/pci.c b/arch/mips/ddb5xxx/common/pci.c index 5c1e07f6edbb..c6a453cff051 100644 --- a/arch/mips/ddb5xxx/common/pci.c +++ b/arch/mips/ddb5xxx/common/pci.c @@ -172,8 +172,8 @@ pcibios_align_resource(void *data, struct resource *res, } void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { /* this should not be called */ MIPS_ASSERT(1 == 0); diff --git a/arch/mips/gt64120/common/pci.c b/arch/mips/gt64120/common/pci.c index c8615e9043a2..15517ff37104 100644 --- a/arch/mips/gt64120/common/pci.c +++ b/arch/mips/gt64120/common/pci.c @@ -785,8 +785,8 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +void pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/mips/ite-boards/generic/it8172_pci.c b/arch/mips/ite-boards/generic/it8172_pci.c index 7a7075b1d728..7a9a942ca7df 100644 --- a/arch/mips/ite-boards/generic/it8172_pci.c +++ b/arch/mips/ite-boards/generic/it8172_pci.c @@ -197,8 +197,8 @@ pcibios_setup(char *str) } void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { unsigned long where, size; u32 reg; @@ -206,7 +206,8 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root, where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + /* FIXME - this doesn't work for PCI-PCI bridges. */ + reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); pci_write_config_dword(dev, where, reg); } diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c index 81aea7026fa2..d6daceee99ba 100644 --- a/arch/mips/kernel/pci.c +++ b/arch/mips/kernel/pci.c @@ -168,8 +168,8 @@ pcibios_align_resource(void *data, struct resource *res, } void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { /* this should not be called */ } diff --git a/arch/mips/mips-boards/generic/pci.c b/arch/mips/mips-boards/generic/pci.c index c9e84b11d87e..36e90a299322 100644 --- a/arch/mips/mips-boards/generic/pci.c +++ b/arch/mips/mips-boards/generic/pci.c @@ -249,8 +249,8 @@ struct pci_fixup pcibios_fixups[] = { }; void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { unsigned long where, size; u32 reg; @@ -258,7 +258,8 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root, where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + /* FIXME - this doesn't work for PCI-PCI bridges. */ + reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); pci_write_config_dword(dev, where, reg); } diff --git a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c index 008f36a4e4ed..26d0b20a7525 100644 --- a/arch/mips/sni/pci.c +++ b/arch/mips/sni/pci.c @@ -138,8 +138,8 @@ pcibios_fixup_bus(struct pci_bus *b) } void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/mips64/mips-boards/generic/pci.c b/arch/mips64/mips-boards/generic/pci.c index 4e377c3e9716..a0f42b33a175 100644 --- a/arch/mips64/mips-boards/generic/pci.c +++ b/arch/mips64/mips-boards/generic/pci.c @@ -308,8 +308,8 @@ struct pci_fixup pcibios_fixups[] = { }; void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { unsigned long where, size; u32 reg; @@ -317,7 +317,8 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root, where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + /* FIXME - this doesn't work for PCI-PCI bridges. */ + reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); pci_write_config_dword(dev, where, reg); } diff --git a/arch/mips64/sgi-ip27/ip27-pci.c b/arch/mips64/sgi-ip27/ip27-pci.c index 61decb894697..5b26dca1771e 100644 --- a/arch/mips64/sgi-ip27/ip27-pci.c +++ b/arch/mips64/sgi-ip27/ip27-pci.c @@ -215,8 +215,8 @@ pcibios_update_irq(struct pci_dev *dev, int irq) } void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { unsigned long where, size; u32 reg; @@ -224,7 +224,8 @@ pcibios_update_resource(struct pci_dev *dev, struct resource *root, where = PCI_BASE_ADDRESS_0 + (resource * 4); size = res->end - res->start; pci_read_config_dword(dev, where, ®); - reg = (reg & size) | (((u32)(res->start - root->start)) & ~size); + /* FIXME - this doesn't work for PCI-PCI bridges. */ + reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); pci_write_config_dword(dev, where, reg); } diff --git a/arch/mips64/sgi-ip32/ip32-pci.c b/arch/mips64/sgi-ip32/ip32-pci.c index f3f2be83974d..11fd67105e61 100644 --- a/arch/mips64/sgi-ip32/ip32-pci.c +++ b/arch/mips64/sgi-ip32/ip32-pci.c @@ -333,8 +333,8 @@ void __init pcibios_align_resource (void *data, struct resource *res, { } -void __init pcibios_update_resource (struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +void __init pcibios_update_resource (struct pci_dev *dev, struct resource *res, + int resource) { } diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 52eeb2434cd7..a457f1d67c25 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -211,7 +211,6 @@ void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) void __devinit pcibios_update_resource( struct pci_dev *dev, - struct resource *root, struct resource *res, int barnum ) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index ee68f236114b..71effba667d2 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -94,8 +94,8 @@ fixup_broken_pcnet32(struct pci_dev* dev) } void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 0b806985b431..fed8f78fd6bb 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -133,8 +133,8 @@ void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *pbus, } void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/sh/kernel/pcibios.c b/arch/sh/kernel/pcibios.c index cfd0adb401fb..6f4d7b84c32d 100644 --- a/arch/sh/kernel/pcibios.c +++ b/arch/sh/kernel/pcibios.c @@ -27,8 +27,8 @@ #include void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index b7e3c63efd9f..9c1ebf20487c 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -856,8 +856,8 @@ char * __init pcibios_setup(char *str) /* */ -void pcibios_update_resource(struct pci_dev *pdev, struct resource *res1, - struct resource *res2, int index) +void pcibios_update_resource(struct pci_dev *pdev, struct resource *res, + int index) { } diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 89901b4bb74c..86288ba4e3c2 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -470,8 +470,8 @@ int pci_assign_resource(struct pci_dev *pdev, int resource) return err; } -void pcibios_update_resource(struct pci_dev *pdev, struct resource *res1, - struct resource *res2, int index) +void pcibios_update_resource(struct pci_dev *pdev, struct resource *res, + int index) { } diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c index 6fc07ccd955e..56acfce6b449 100644 --- a/arch/v850/kernel/rte_mb_a_pci.c +++ b/arch/v850/kernel/rte_mb_a_pci.c @@ -288,8 +288,7 @@ void __devinit pcibios_update_irq (struct pci_dev *dev, int irq) } void __nomods_init -pcibios_update_resource (struct pci_dev *dev, struct resource *root, - struct resource *r, int resource) +pcibios_update_resource (struct pci_dev *dev, struct resource *r, int resource) { u32 new, check; int reg; diff --git a/arch/x86_64/pci/x86-64.c b/arch/x86_64/pci/x86-64.c index 9c29fa694796..f9ee1dd44061 100644 --- a/arch/x86_64/pci/x86-64.c +++ b/arch/x86_64/pci/x86-64.c @@ -34,8 +34,8 @@ #include "pci.h" void -pcibios_update_resource(struct pci_dev *dev, struct resource *root, - struct resource *res, int resource) +pcibios_update_resource(struct pci_dev *dev, struct resource *res, + int resource) { u32 new, check; int reg; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3331a411db7b..be89db151d90 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -90,9 +90,9 @@ int pci_assign_resource(struct pci_dev *dev, int resno) resno, res->start, res->end, dev->slot_name); } else { DBGC((KERN_ERR " got res[%lx:%lx] for resource %d of %s\n", - res->start, res->end, i, dev->dev.name)); + res->start, res->end, resno, dev->dev.name)); /* Update PCI config space. */ - pcibios_update_resource(dev, res->parent, res, resno); + pcibios_update_resource(dev, res, resno); } return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index 393417af2f89..0bc50220c102 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -531,8 +531,7 @@ char *pcibios_setup (char *str); /* Used only when drivers/pci/setup.c is used */ void pcibios_align_resource(void *, struct resource *, unsigned long, unsigned long); -void pcibios_update_resource(struct pci_dev *, struct resource *, - struct resource *, int); +void pcibios_update_resource(struct pci_dev *, struct resource *, int); void pcibios_update_irq(struct pci_dev *, int irq); void pcibios_fixup_pbus_ranges(struct pci_bus *, struct pbus_set_ranges_data *); -- cgit v1.2.3 From 2ef9c88bfaee6fbd7f9d5da32f9b93794be49185 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 21:56:53 +0000 Subject: [PCI] pci-8: pci_resource_to_bus() Convert pcibios_fixup_pbus_ranges() into something more generic, namely pcibios_resource_to_bus() - we are really trying to convert resources to something to program into bus registers for bridge windows, and in fact, PCI device BARs. This is necessary since some architectures, namely Alpha, ARM and PARISC have an offset between PCI addressing and host-based addressing, so resources need to be adjusted when read or when written back to the bus. We provide a generic version in asm-generic/pci.h, which most architectures use. This patch finds the following architectures with something to think consider: - ppc, ppc64 adjusts resources for devices, but not buses. This is inconsistent, and leads to improperly programmed windows/BARs. PPC people (Anton) has a replacement PCI resource implementation which should do the right thing. --- arch/alpha/kernel/pci.c | 28 ++++++++++++++----------- arch/arm/kernel/bios32.c | 24 ++++++++++++++------- arch/i386/pci/common.c | 5 ----- arch/ia64/pci/pci.c | 5 ----- arch/mips/ddb5074/pci.c | 5 ----- arch/mips/ddb5476/pci.c | 9 ++++---- arch/mips64/sgi-ip27/ip27-pci.c | 6 ------ arch/mips64/sgi-ip32/ip32-pci.c | 6 ------ arch/parisc/kernel/pci.c | 46 ++++++++++++++++++++++++----------------- arch/ppc/kernel/pci.c | 5 ----- arch/ppc64/kernel/pci.c | 5 ----- arch/sh/kernel/pci-dc.c | 4 ---- arch/sh/kernel/pci-sh7751.c | 6 ------ arch/sh/kernel/pci_st40.c | 6 ------ arch/sh/kernel/pcibios.c | 1 - drivers/pci/setup-bus.c | 41 ++++++++++++++---------------------- include/asm-alpha/pci.h | 5 +++++ include/asm-arm/pci.h | 4 ++++ include/asm-generic/pci.h | 25 ++++++++++++++++++++++ include/asm-i386/pci.h | 3 +++ include/asm-ia64/pci.h | 3 +++ include/asm-mips/pci.h | 3 +++ include/asm-mips64/pci.h | 4 ++++ include/asm-parisc/pci.h | 4 ++++ include/asm-ppc/pci.h | 3 +++ include/asm-ppc64/pci.h | 3 +++ include/asm-sh/pci.h | 2 ++ include/asm-sparc64/pci.h | 3 +++ include/asm-x86_64/pci.h | 3 +++ include/linux/pci.h | 9 +++----- 30 files changed, 148 insertions(+), 128 deletions(-) create mode 100644 include/asm-generic/pci.h (limited to 'drivers') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 81e079568688..10942ee45105 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -12,7 +12,7 @@ * Nov 2000, Ivan Kokshaysky * PCI-PCI bridges cleanup */ - +#include #include #include #include @@ -334,21 +334,25 @@ common_swizzle(struct pci_dev *dev, u8 *pinp) } void __devinit -pcibios_fixup_pbus_ranges(struct pci_bus * bus, - struct pbus_set_ranges_data * ranges) +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) { - struct pci_controller *hose = (struct pci_controller *)bus->sysdata; + struct pci_controller *hose = (struct pci_controller *)dev->sysdata; + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) + offset = hose->io_space->start; + else if (res->flags & IORESOURCE_MEM) + offset = hose->mem_space->start; - ranges->io_start -= hose->io_space->start; - ranges->io_end -= hose->io_space->start; - ranges->mem_start -= hose->mem_space->start; - ranges->mem_end -= hose->mem_space->start; -/* FIXME: On older alphas we could use dense memory space - to access prefetchable resources. */ - ranges->prefetch_start -= hose->mem_space->start; - ranges->prefetch_end -= hose->mem_space->start; + region->start = res->start - offset; + region->end = res->end - offset; } +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pcibios_resource_to_bus); +#endif + int pcibios_enable_device(struct pci_dev *dev, int mask) { diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 8df79c2cdee3..090a29fee3e7 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -439,18 +439,26 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) * Convert from Linux-centric to bus-centric addresses for bridge devices. */ void __devinit -pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) { - struct pci_sys_data *root = bus->sysdata; + struct pci_sys_data *root = dev->sysdata; + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) + offset = root->io_offset; + if (res->flags & IORESOURCE_MEM) + offset = root->mem_offset; - ranges->io_start -= root->io_offset; - ranges->io_end -= root->io_offset; - ranges->mem_start -= root->mem_offset; - ranges->mem_end -= root->mem_offset; - ranges->prefetch_start -= root->mem_offset; - ranges->prefetch_end -= root->mem_offset; + region->start = res->start - offset; + region->end = res->end - offset; } +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pcibios_fixup_bus); +EXPORT_SYMBOL(pcibios_resource_to_bus); +#endif + /* * This is the standard PCI-PCI bridge swizzling algorithm: * diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index 6d65faac8ec7..290770516833 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -90,11 +90,6 @@ static void __devinit pcibios_fixup_ghosts(struct pci_bus *b) } } -void __devinit -pcibios_fixup_pbus_ranges (struct pci_bus *bus, struct pbus_set_ranges_data *ranges) -{ -} - /* * Called after each bus is probed, but before its children * are examined. diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 84e49ffc52b1..03fcd62072f5 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -168,11 +168,6 @@ pcibios_update_irq (struct pci_dev *dev, int irq) /* ??? FIXME -- record old value for shutdown. */ } -void __devinit -pcibios_fixup_pbus_ranges (struct pci_bus * bus, struct pbus_set_ranges_data * ranges) -{ -} - static inline int pcibios_enable_resources (struct pci_dev *dev, int mask) { diff --git a/arch/mips/ddb5074/pci.c b/arch/mips/ddb5074/pci.c index b97df65f4f3f..6baf4bea33cf 100644 --- a/arch/mips/ddb5074/pci.c +++ b/arch/mips/ddb5074/pci.c @@ -297,11 +297,6 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *bus, - struct pbus_set_ranges_data *ranges) -{ -} - int pcibios_enable_resources(struct pci_dev *dev) { u16 cmd, old_cmd; diff --git a/arch/mips/ddb5476/pci.c b/arch/mips/ddb5476/pci.c index 473aa79e4d65..08f4a867c03f 100644 --- a/arch/mips/ddb5476/pci.c +++ b/arch/mips/ddb5476/pci.c @@ -341,8 +341,10 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *bus, - struct pbus_set_ranges_data *ranges) +#if 0 /* original DDB5074 code */ +void __devinit +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) { /* * our caller figure out range by going through the dev structures. @@ -350,15 +352,14 @@ void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *bus, * different view of the addressing space. */ -#if 0 /* original DDB5074 code */ if (bus->number == 0) { ranges->io_start -= bus->resource[0]->start; ranges->io_end -= bus->resource[0]->start; ranges->mem_start -= bus->resource[1]->start; ranges->mem_end -= bus->resource[1]->start; } -#endif } +#endif int pcibios_enable_resources(struct pci_dev *dev) { diff --git a/arch/mips64/sgi-ip27/ip27-pci.c b/arch/mips64/sgi-ip27/ip27-pci.c index 5b26dca1771e..328fe39a12a1 100644 --- a/arch/mips64/sgi-ip27/ip27-pci.c +++ b/arch/mips64/sgi-ip27/ip27-pci.c @@ -235,12 +235,6 @@ pcibios_fixup_bus(struct pci_bus *b) pci_fixup_irqs(pci_swizzle, pci_map_irq); } -void __devinit -pcibios_fixup_pbus_ranges(struct pci_bus * bus, - struct pbus_set_ranges_data * ranges) -{ -} - int __init pcibios_enable_device(struct pci_dev *dev) { diff --git a/arch/mips64/sgi-ip32/ip32-pci.c b/arch/mips64/sgi-ip32/ip32-pci.c index 11fd67105e61..2aa4aeccdd74 100644 --- a/arch/mips64/sgi-ip32/ip32-pci.c +++ b/arch/mips64/sgi-ip32/ip32-pci.c @@ -348,12 +348,6 @@ void __init pcibios_fixup_bus (struct pci_bus *b) pci_fixup_irqs (macepci_swizzle, macepci_map_irq); } -/* XXX anybody know what this is supposed to do? */ -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus * bus, - struct pbus_set_ranges_data * ranges) -{ -} - /* * Handle errors from the bridge. This includes master and target aborts, * various command and address errors, and the interrupt test. This gets diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index a457f1d67c25..67bf315c62e1 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -344,30 +344,34 @@ pcibios_link_hba_resources( struct resource *hba_res, struct resource *r) /* ** called by drivers/pci/setup-res.c:pci_setup_bridge(). */ -void __devinit pcibios_fixup_pbus_ranges( - struct pci_bus *bus, - struct pbus_set_ranges_data *ranges +void __devinit pcibios_resource_to_bus( + struct pci_dev *dev, + struct pci_bus_region *region, + struct resource *res ) { + struct pci_bus *bus = dev->bus; struct pci_hba_data *hba = HBA_DATA(bus->dev->platform_data); - /* - ** I/O space may see busnumbers here. Something - ** in the form of 0xbbxxxx where bb is the bus num - ** and xxxx is the I/O port space address. - ** Remaining address translation are done in the - ** PCI Host adapter specific code - ie dino_out8. - */ - ranges->io_start = PCI_PORT_ADDR(ranges->io_start); - ranges->io_end = PCI_PORT_ADDR(ranges->io_end); - - /* Convert MMIO addr to PCI addr (undo global virtualization) */ - ranges->mem_start = PCI_BUS_ADDR(hba, ranges->mem_start); - ranges->mem_end = PCI_BUS_ADDR(hba, ranges->mem_end); + if (res->flags & IORESOURCE_IO) { + /* + ** I/O space may see busnumbers here. Something + ** in the form of 0xbbxxxx where bb is the bus num + ** and xxxx is the I/O port space address. + ** Remaining address translation are done in the + ** PCI Host adapter specific code - ie dino_out8. + */ + region->start = PCI_PORT_ADDR(res->start); + region->end = PCI_PORT_ADDR(res->end); + } else if (res->flags & IORESOURCE_MEM) { + /* Convert MMIO addr to PCI addr (undo global virtualization) */ + region->start = PCI_BUS_ADDR(hba, res->start); + region->end = PCI_BUS_ADDR(hba, res->end); + } - DBG_RES("pcibios_fixup_pbus_ranges(%02x, [%lx,%lx %lx,%lx])\n", bus->number, - ranges->io_start, ranges->io_end, - ranges->mem_start, ranges->mem_end); + DBG_RES("pcibios_resource_to_bus(%02x %s [%lx,%lx])\n", + bus->number, res->flags & IORESOURCE_IO ? "IO" : "MEM", + region->start, region->end); /* KLUGE ALERT ** if this resource isn't linked to a "parent", then it seems @@ -377,6 +381,10 @@ void __devinit pcibios_fixup_pbus_ranges( pcibios_link_hba_resources(&hba->lmmio_space, bus->resource[1]); } +#ifdef CONFIG_HOTPLUG +EXPORT_SYMBOL(pcibios_resource_to_bus); +#endif + #define MAX(val1, val2) ((val1) > (val2) ? (val1) : (val2)) diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 71effba667d2..d35111a4bf38 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1107,11 +1107,6 @@ common_swizzle(struct pci_dev *dev, unsigned char *pinp) return PCI_SLOT(dev->devfn); } -void __devinit -pcibios_fixup_pbus_ranges(struct pci_bus * bus, struct pbus_set_ranges_data * ranges) -{ -} - unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, unsigned long start, unsigned long size) { diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index fed8f78fd6bb..7edf23857c24 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -127,11 +127,6 @@ struct pci_dev *pci_find_dev_by_addr(unsigned long addr) return NULL; } -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *pbus, - struct pbus_set_ranges_data *pranges) -{ -} - void pcibios_update_resource(struct pci_dev *dev, struct resource *res, int resource) diff --git a/arch/sh/kernel/pci-dc.c b/arch/sh/kernel/pci-dc.c index 34ec18b09253..ed3bcf26f810 100644 --- a/arch/sh/kernel/pci-dc.c +++ b/arch/sh/kernel/pci-dc.c @@ -113,10 +113,6 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size, } -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) -{ -} - void __init pcibios_fixup_bus(struct pci_bus *bus) { struct list_head *ln; diff --git a/arch/sh/kernel/pci-sh7751.c b/arch/sh/kernel/pci-sh7751.c index 9db302503b0a..19a8291aa359 100644 --- a/arch/sh/kernel/pci-sh7751.c +++ b/arch/sh/kernel/pci-sh7751.c @@ -250,12 +250,6 @@ struct pci_fixup pcibios_fixups[] = { { 0 } }; -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *b, - struct pbus_set_ranges_data *range) -{ - /* No fixups needed */ -} - /* * Called after each bus is probed, but before its children * are examined. diff --git a/arch/sh/kernel/pci_st40.c b/arch/sh/kernel/pci_st40.c index ccef05c9e87c..b1e8e2b1c1f8 100644 --- a/arch/sh/kernel/pci_st40.c +++ b/arch/sh/kernel/pci_st40.c @@ -380,12 +380,6 @@ static int __init map_harp_irq(struct pci_dev *dev, u8 slot, u8 pin) } -void __devinit -pcibios_fixup_pbus_ranges(struct pci_bus *bus, - struct pbus_set_ranges_data *ranges) -{ -} - void __init pcibios_init(void) { extern unsigned long memory_start, memory_end; diff --git a/arch/sh/kernel/pcibios.c b/arch/sh/kernel/pcibios.c index 6f4d7b84c32d..6b785b914ab4 100644 --- a/arch/sh/kernel/pcibios.c +++ b/arch/sh/kernel/pcibios.c @@ -19,7 +19,6 @@ * pcibios_fixup_bus() * pcibios_init() * pcibios_setup() - * pcibios_fixup_pbus_ranges() */ #include diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b796e2270a65..0eab0b57e10a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -72,40 +72,29 @@ pbus_assign_resources_sorted(struct pci_bus *bus) requires that if there is no I/O ports or memory behind the bridge, corresponding range must be turned off by writing base value greater than limit to the bridge's base/limit registers. */ -static void __devinit -pci_setup_bridge(struct pci_bus *bus) +static void __devinit pci_setup_bridge(struct pci_bus *bus) { - struct pbus_set_ranges_data ranges; struct pci_dev *bridge = bus->self; + struct pci_bus_region region; u32 l; - if (!bridge || (bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) - return; - - ranges.io_start = bus->resource[0]->start; - ranges.io_end = bus->resource[0]->end; - ranges.mem_start = bus->resource[1]->start; - ranges.mem_end = bus->resource[1]->end; - ranges.prefetch_start = bus->resource[2]->start; - ranges.prefetch_end = bus->resource[2]->end; - pcibios_fixup_pbus_ranges(bus, &ranges); - DBGC((KERN_INFO "PCI: Bus %d, bridge: %s\n", bus->number, bridge->dev.name)); /* Set up the top and bottom of the PCI I/O segment for this bus. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[0]); if (bus->resource[0]->flags & IORESOURCE_IO) { pci_read_config_dword(bridge, PCI_IO_BASE, &l); l &= 0xffff0000; - l |= (ranges.io_start >> 8) & 0x00f0; - l |= ranges.io_end & 0xf000; + l |= (region.start >> 8) & 0x00f0; + l |= region.end & 0xf000; /* Set up upper 16 bits of I/O base/limit. */ pci_write_config_word(bridge, PCI_IO_BASE_UPPER16, - ranges.io_start >> 16); + region.start >> 16); pci_write_config_word(bridge, PCI_IO_LIMIT_UPPER16, - ranges.io_end >> 16); + region.end >> 16); DBGC((KERN_INFO " IO window: %04lx-%04lx\n", - ranges.io_start, ranges.io_end)); + region.start, region.end)); } else { /* Clear upper 16 bits of I/O base/limit. */ @@ -117,11 +106,12 @@ pci_setup_bridge(struct pci_bus *bus) /* Set up the top and bottom of the PCI Memory segment for this bus. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[1]); if (bus->resource[1]->flags & IORESOURCE_MEM) { - l = (ranges.mem_start >> 16) & 0xfff0; - l |= ranges.mem_end & 0xfff00000; + l = (region.start >> 16) & 0xfff0; + l |= region.end & 0xfff00000; DBGC((KERN_INFO " MEM window: %08lx-%08lx\n", - ranges.mem_start, ranges.mem_end)); + region.start, region.end)); } else { l = 0x0000fff0; @@ -134,11 +124,12 @@ pci_setup_bridge(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); /* Set up PREF base/limit. */ + pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]); if (bus->resource[2]->flags & IORESOURCE_PREFETCH) { - l = (ranges.prefetch_start >> 16) & 0xfff0; - l |= ranges.prefetch_end & 0xfff00000; + l = (region.start >> 16) & 0xfff0; + l |= region.end & 0xfff00000; DBGC((KERN_INFO " PREFETCH window: %08lx-%08lx\n", - ranges.prefetch_start, ranges.prefetch_end)); + region.start, region.end)); } else { l = 0x0000fff0; diff --git a/include/asm-alpha/pci.h b/include/asm-alpha/pci.h index 3f88b46f8dfe..1e82bf33cb84 100644 --- a/include/asm-alpha/pci.h +++ b/include/asm-alpha/pci.h @@ -190,6 +190,11 @@ pci_dac_dma_sync_single(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, /* Return the index of the PCI controller for device PDEV. */ extern int pci_controller_num(struct pci_dev *pdev); + +extern void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); + #endif /* __KERNEL__ */ /* Values for the `which' argument to sys_pciconfig_iobase. */ diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h index 193c96e1609f..72843a2888b5 100644 --- a/include/asm-arm/pci.h +++ b/include/asm-arm/pci.h @@ -175,6 +175,10 @@ void pci_pool_free (struct pci_pool *pool, void *vaddr, dma_addr_t addr); extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); +extern void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); + #endif /* __KERNEL__ */ #endif diff --git a/include/asm-generic/pci.h b/include/asm-generic/pci.h new file mode 100644 index 000000000000..df757d240e9a --- /dev/null +++ b/include/asm-generic/pci.h @@ -0,0 +1,25 @@ +/* + * linux/include/asm-generic/pci.h + * + * Copyright (C) 2003 Russell King + */ +#ifndef _ASM_GENERIC_PCI_H +#define _ASM_GENERIC_PCI_H + +/** + * pcibios_resource_to_bus - convert resource to PCI bus address + * @dev: device which owns this resource + * @region: converted bus-centric region (start,end) + * @res: resource to convert + * + * Convert a resource to a PCI device bus address or bus window. + */ +static inline void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + region->start = res->start; + region->end = res->end; +} + +#endif diff --git a/include/asm-i386/pci.h b/include/asm-i386/pci.h index d41feaba85ef..e0e0442260df 100644 --- a/include/asm-i386/pci.h +++ b/include/asm-i386/pci.h @@ -105,4 +105,7 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, /* implement the pci_ DMA API in terms of the generic device dma_ one */ #include +/* generic pci stuff */ +#include + #endif /* __i386_PCI_H */ diff --git a/include/asm-ia64/pci.h b/include/asm-ia64/pci.h index 62a7d5a544d5..69a58612d87b 100644 --- a/include/asm-ia64/pci.h +++ b/include/asm-ia64/pci.h @@ -97,4 +97,7 @@ extern int pcibios_prep_mwi (struct pci_dev *); extern int pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); +/* generic pci stuff */ +#include + #endif /* _ASM_IA64_PCI_H */ diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 84c9bd2b9bfb..98b248e8cd29 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -252,4 +252,7 @@ extern inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* _ASM_PCI_H */ diff --git a/include/asm-mips64/pci.h b/include/asm-mips64/pci.h index a7cc388c5824..730e04075480 100644 --- a/include/asm-mips64/pci.h +++ b/include/asm-mips64/pci.h @@ -272,4 +272,8 @@ static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* _ASM_PCI_H */ + diff --git a/include/asm-parisc/pci.h b/include/asm-parisc/pci.h index 2dbd703983e7..d44ad70d0e35 100644 --- a/include/asm-parisc/pci.h +++ b/include/asm-parisc/pci.h @@ -186,4 +186,8 @@ extern inline void pcibios_register_hba(struct pci_hba_data *x) /* export the pci_ DMA API in terms of the dma_ one */ #include +extern void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); + #endif /* __ASM_PARISC_PCI_H */ diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h index 92118321f0a5..c178b8e5d4b3 100644 --- a/include/asm-ppc/pci.h +++ b/include/asm-ppc/pci.h @@ -273,4 +273,7 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* __PPC_PCI_H */ diff --git a/include/asm-ppc64/pci.h b/include/asm-ppc64/pci.h index 0b715895f043..2cab41c41cca 100644 --- a/include/asm-ppc64/pci.h +++ b/include/asm-ppc64/pci.h @@ -124,4 +124,7 @@ int pci_mmap_page_range(struct pci_dev *pdev, struct vm_area_struct *vma, #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* __PPC64_PCI_H */ diff --git a/include/asm-sh/pci.h b/include/asm-sh/pci.h index f9690c521d19..60fffd091ad6 100644 --- a/include/asm-sh/pci.h +++ b/include/asm-sh/pci.h @@ -240,6 +240,8 @@ static inline int pci_dma_supported(struct pci_dev *hwdev, u64 mask) #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include #endif /* __ASM_SH_PCI_H */ diff --git a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h index 53a010006c41..6b97445a221a 100644 --- a/include/asm-sparc64/pci.h +++ b/include/asm-sparc64/pci.h @@ -207,4 +207,7 @@ extern int pcibios_prep_mwi(struct pci_dev *dev); #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* __SPARC64_PCI_H */ diff --git a/include/asm-x86_64/pci.h b/include/asm-x86_64/pci.h index 024a9c045681..74594c63e38c 100644 --- a/include/asm-x86_64/pci.h +++ b/include/asm-x86_64/pci.h @@ -279,4 +279,7 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, #endif /* __KERNEL__ */ +/* generic pci stuff */ +#include + #endif /* __x8664_PCI_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 0bc50220c102..b01ae2e4df9c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -485,11 +485,9 @@ struct pci_ops { int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); }; -struct pbus_set_ranges_data -{ - unsigned long io_start, io_end; - unsigned long mem_start, mem_end; - unsigned long prefetch_start, prefetch_end; +struct pci_bus_region { + unsigned long start; + unsigned long end; }; struct pci_driver { @@ -533,7 +531,6 @@ void pcibios_align_resource(void *, struct resource *, unsigned long, unsigned long); void pcibios_update_resource(struct pci_dev *, struct resource *, int); void pcibios_update_irq(struct pci_dev *, int irq); -void pcibios_fixup_pbus_ranges(struct pci_bus *, struct pbus_set_ranges_data *); /* Generic PCI functions used internally */ -- cgit v1.2.3 From b64958d945540262ad39396e633e94b1a896771f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 22:17:24 +0000 Subject: [PCI] pci-9: Kill per-architecture pcibios_update_resource() Kill pcibios_update_resource(), replacing it with pci_update_resource(). pci_update_resource() uses pcibios_resource_to_bus() to convert a resource to a device BAR - the transformation should be exactly the same as the transformation used for the PCI bridges. pci_update_resource "knows" about 64-bit BARs, but doesn't attempt to set the high 32-bits to anything non-zero - currently no architecture attempts to do something different. If anyone cares, please fix; I'm going to reflect current behaviour for the time being. Ivan pointed out the following architectures need to examine their pcibios_update_resource() implementation - they should make sure that this new implementation does the right thing. #warning's have been added where appropriate. ia64 mips mips64 This cset also includes a fix for the problem reported by AKPM where 64-bit arch compilers complain about the resource mask being placed in a u32. --- arch/alpha/kernel/pci.c | 39 ------------------ arch/arm/kernel/bios32.c | 41 ------------------- arch/i386/pci/i386.c | 28 ------------- arch/ia64/pci/pci.c | 17 +------- arch/mips/ddb5074/pci.c | 31 -------------- arch/mips/ddb5476/pci.c | 31 -------------- arch/mips/ddb5xxx/common/pci.c | 8 ---- arch/mips/gt64120/common/pci.c | 33 --------------- arch/mips/ite-boards/generic/it8172_pci.c | 15 +------ arch/mips/kernel/pci.c | 7 ---- arch/mips/mips-boards/generic/pci.c | 15 +------ arch/mips/sni/pci.c | 28 ------------- arch/mips64/mips-boards/generic/pci.c | 15 +------ arch/mips64/sgi-ip27/ip27-pci.c | 15 +------ arch/mips64/sgi-ip32/ip32-pci.c | 5 --- arch/parisc/kernel/pci.c | 67 ------------------------------- arch/ppc/kernel/pci.c | 40 ------------------ arch/ppc64/kernel/pci.c | 31 -------------- arch/sh/kernel/pcibios.c | 29 ------------- arch/sparc/kernel/pcic.c | 7 ---- arch/sparc64/kernel/pci.c | 10 ----- arch/v850/kernel/rte_mb_a_pci.c | 47 ++++++---------------- arch/x86_64/pci/x86-64.c | 28 ------------- drivers/pci/setup-res.c | 58 ++++++++++++++++++++++++-- include/linux/pci.h | 1 - 25 files changed, 71 insertions(+), 575 deletions(-) (limited to 'drivers') diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index 10942ee45105..b52bc3719803 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -264,45 +264,6 @@ pcibios_fixup_bus(struct pci_bus *bus) } } -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - struct pci_controller *hose = dev->sysdata; - struct resource *root; - int where; - u32 reg; - - if (resource < PCI_ROM_RESOURCE) - where = PCI_BASE_ADDRESS_0 + (resource * 4); - else if (resource == PCI_ROM_RESOURCE) - where = dev->rom_base_reg; - else { - return; /* Don't update non-standard resources here. */ - } - - /* Point root at the hose root. */ - if (res->flags & IORESOURCE_IO) - root = hose->io_space; - else if (res->flags & IORESOURCE_MEM) - root = hose->mem_space; - else { - return; /* Don't update non-standard resources here. */ - } - - reg = (res->start - root->start) | (res->flags & 0xf); - pci_write_config_dword(dev, where, reg); - if ((res->flags & (PCI_BASE_ADDRESS_SPACE - | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) - == (PCI_BASE_ADDRESS_SPACE_MEMORY - | PCI_BASE_ADDRESS_MEM_TYPE_64)) { - pci_write_config_dword(dev, where+4, 0); - printk(KERN_WARNING "PCI: dev %s type 64-bit\n", dev->dev.name); - } - - /* ??? FIXME -- record old value for shutdown. */ -} - void __init pcibios_update_irq(struct pci_dev *dev, int irq) { diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 090a29fee3e7..265b3c183d5a 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -259,47 +259,6 @@ struct pci_fixup pcibios_fixups[] = { }, { 0 } }; -void __devinit -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - struct pci_sys_data *sys = dev->sysdata; - u32 val, check; - int reg; - - if (debug_pci) - printk("PCI: Assigning %3s %08lx to %s\n", - res->flags & IORESOURCE_IO ? "IO" : "MEM", - res->start, dev->dev.name); - - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a - * non-standard resource. - */ - return; - } - - val = res->start; - if (res->flags & IORESOURCE_MEM) - val -= sys->mem_offset; - else - val -= sys->io_offset; - val |= res->flags & PCI_REGION_FLAG_MASK; - - pci_write_config_dword(dev, reg, val); - pci_read_config_dword(dev, reg, &check); - if ((val ^ check) & ((val & PCI_BASE_ADDRESS_SPACE_IO) ? - PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, - resource, val, check); - } -} - void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) { if (debug_pci) diff --git a/arch/i386/pci/i386.c b/arch/i386/pci/i386.c index 36e5d4e30bbc..b81cf06d9bea 100644 --- a/arch/i386/pci/i386.c +++ b/arch/i386/pci/i386.c @@ -33,34 +33,6 @@ #include "pci.h" -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - /* * We need to avoid collisions with `mirrored' VGA ports * and other strange ISA hardware, so we always want the diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 03fcd62072f5..27757a02feda 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -143,22 +143,7 @@ pcibios_fixup_bus (struct pci_bus *b) return; } -void __devinit -pcibios_update_resource (struct pci_dev *dev, struct resource *res, - int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - /* FIXME - this doesn't work for PCI-PCI bridges. */ - reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); - pci_write_config_dword(dev, where, reg); - - /* ??? FIXME -- record old value for shutdown. */ -} +#warning pcibios_update_resource() is now a generic implementation - please check void __devinit pcibios_update_irq (struct pci_dev *dev, int irq) diff --git a/arch/mips/ddb5074/pci.c b/arch/mips/ddb5074/pci.c index 6baf4bea33cf..d57926842c6f 100644 --- a/arch/mips/ddb5074/pci.c +++ b/arch/mips/ddb5074/pci.c @@ -336,37 +336,6 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4 * resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* - * Somebody might have asked allocation of a non-standard - * resource - */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & - ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : - PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { diff --git a/arch/mips/ddb5476/pci.c b/arch/mips/ddb5476/pci.c index 08f4a867c03f..6290eff02793 100644 --- a/arch/mips/ddb5476/pci.c +++ b/arch/mips/ddb5476/pci.c @@ -400,37 +400,6 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4 * resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* - * Somebody might have asked allocation of a non-standard - * resource - */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & - ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : - PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { diff --git a/arch/mips/ddb5xxx/common/pci.c b/arch/mips/ddb5xxx/common/pci.c index c6a453cff051..4c8457fbbbd1 100644 --- a/arch/mips/ddb5xxx/common/pci.c +++ b/arch/mips/ddb5xxx/common/pci.c @@ -170,11 +170,3 @@ pcibios_align_resource(void *data, struct resource *res, /* this should not be called */ MIPS_ASSERT(1 == 0); } - -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - /* this should not be called */ - MIPS_ASSERT(1 == 0); -} diff --git a/arch/mips/gt64120/common/pci.c b/arch/mips/gt64120/common/pci.c index 15517ff37104..8350b9dad0b1 100644 --- a/arch/mips/gt64120/common/pci.c +++ b/arch/mips/gt64120/common/pci.c @@ -785,39 +785,6 @@ int pcibios_enable_device(struct pci_dev *dev) return pcibios_enable_resources(dev); } -void pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - return; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4 * resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* - * Somebody might have asked allocation of a non-standard - * resource - */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & - ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : - PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { diff --git a/arch/mips/ite-boards/generic/it8172_pci.c b/arch/mips/ite-boards/generic/it8172_pci.c index 7a9a942ca7df..6073dfcb36b8 100644 --- a/arch/mips/ite-boards/generic/it8172_pci.c +++ b/arch/mips/ite-boards/generic/it8172_pci.c @@ -196,20 +196,7 @@ pcibios_setup(char *str) return str; } -void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - /* FIXME - this doesn't work for PCI-PCI bridges. */ - reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); - pci_write_config_dword(dev, where, reg); -} +#warning pcibios_update_resource() is now a generic implementation - please check void __init pcibios_fixup_bus(struct pci_bus *b) { diff --git a/arch/mips/kernel/pci.c b/arch/mips/kernel/pci.c index d6daceee99ba..b7b72fd1a267 100644 --- a/arch/mips/kernel/pci.c +++ b/arch/mips/kernel/pci.c @@ -166,10 +166,3 @@ pcibios_align_resource(void *data, struct resource *res, { /* this should not be called */ } - -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - /* this should not be called */ -} diff --git a/arch/mips/mips-boards/generic/pci.c b/arch/mips/mips-boards/generic/pci.c index 36e90a299322..2faf1bc5376c 100644 --- a/arch/mips/mips-boards/generic/pci.c +++ b/arch/mips/mips-boards/generic/pci.c @@ -248,20 +248,7 @@ struct pci_fixup pcibios_fixups[] = { { 0 } }; -void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - /* FIXME - this doesn't work for PCI-PCI bridges. */ - reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); - pci_write_config_dword(dev, where, reg); -} +#warning pcibios_update_resource() is now a generic implementation - please check /* * Called after each bus is probed, but before its children diff --git a/arch/mips/sni/pci.c b/arch/mips/sni/pci.c index 26d0b20a7525..8bdc9324559d 100644 --- a/arch/mips/sni/pci.c +++ b/arch/mips/sni/pci.c @@ -137,34 +137,6 @@ pcibios_fixup_bus(struct pci_bus *b) { } -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - void __init pcibios_init(void) { struct pci_ops *ops = &sni_pci_ops; diff --git a/arch/mips64/mips-boards/generic/pci.c b/arch/mips64/mips-boards/generic/pci.c index a0f42b33a175..93ffe1ff3f7f 100644 --- a/arch/mips64/mips-boards/generic/pci.c +++ b/arch/mips64/mips-boards/generic/pci.c @@ -307,20 +307,7 @@ struct pci_fixup pcibios_fixups[] = { { 0 } }; -void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - /* FIXME - this doesn't work for PCI-PCI bridges. */ - reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); - pci_write_config_dword(dev, where, reg); -} +#warning pcibios_update_resource() is now a generic implementation - please check unsigned __init int pcibios_assign_all_busses(void) { diff --git a/arch/mips64/sgi-ip27/ip27-pci.c b/arch/mips64/sgi-ip27/ip27-pci.c index 328fe39a12a1..55f0234e87e0 100644 --- a/arch/mips64/sgi-ip27/ip27-pci.c +++ b/arch/mips64/sgi-ip27/ip27-pci.c @@ -214,20 +214,7 @@ pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -void __init -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - unsigned long where, size; - u32 reg; - - where = PCI_BASE_ADDRESS_0 + (resource * 4); - size = res->end - res->start; - pci_read_config_dword(dev, where, ®); - /* FIXME - this doesn't work for PCI-PCI bridges. */ - reg = (reg & size) | (((u32)(res->start - res->parent->start)) & ~size); - pci_write_config_dword(dev, where, reg); -} +#warning pcibios_update_resource() is now a generic implementation - please check void __init pcibios_fixup_bus(struct pci_bus *b) diff --git a/arch/mips64/sgi-ip32/ip32-pci.c b/arch/mips64/sgi-ip32/ip32-pci.c index 2aa4aeccdd74..d4af201dc975 100644 --- a/arch/mips64/sgi-ip32/ip32-pci.c +++ b/arch/mips64/sgi-ip32/ip32-pci.c @@ -333,11 +333,6 @@ void __init pcibios_align_resource (void *data, struct resource *res, { } -void __init pcibios_update_resource (struct pci_dev *dev, struct resource *res, - int resource) -{ -} - void __init pcibios_update_irq (struct pci_dev *dev, int irq) { pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq); diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c index 67bf315c62e1..257b8f2b9a54 100644 --- a/arch/parisc/kernel/pci.c +++ b/arch/parisc/kernel/pci.c @@ -192,73 +192,6 @@ void __devinit pcibios_update_irq(struct pci_dev *dev, int irq) } -/* ------------------------------------ -** -** Program one BAR in PCI config space. -** -** ------------------------------------ -** PAT PDC systems need this routine. PA legacy PDC does not. -** -** When BAR's are configured by linux, this routine will update -** configuration space with the "normalized" address. "root" indicates -** where the range starts and res is some portion of that range. -** -** VCLASS: For all PA-RISC systems except V-class, root->start would be zero. -** -** PAT PDC can tell us which MMIO ranges are available or already in use. -** I/O port space and such are not memory mapped anyway for PA-Risc. -*/ -void __devinit -pcibios_update_resource( - struct pci_dev *dev, - struct resource *res, - int barnum - ) -{ - int where; - u32 barval = 0; - - DBG_RES("pcibios_update_resource(%s, ..., %d) [%lx,%lx]/%x\n", - dev->slot_name, - barnum, res->start, res->end, (int) res->flags); - - if (barnum >= PCI_BRIDGE_RESOURCES) { - /* handled in PCI-PCI bridge specific support */ - return; - } - - if (barnum == PCI_ROM_RESOURCE) { - where = PCI_ROM_ADDRESS; - } else { - /* 0-5 standard PCI "regions" */ - where = PCI_BASE_ADDRESS_0 + (barnum * 4); - } - - if (res->flags & IORESOURCE_IO) { - barval = PCI_PORT_ADDR(res->start); - } else if (res->flags & IORESOURCE_MEM) { - barval = PCI_BUS_ADDR(HBA_DATA(dev->bus->dev->platform_data), res->start); - } else { - panic("pcibios_update_resource() WTF? flags not IO or MEM"); - } - - pci_write_config_dword(dev, where, barval); - -/* XXX FIXME - Elroy does support 64-bit (dual cycle) addressing. -** But at least one device (Symbios 53c896) which has 64-bit BAR -** doesn't actually work right with dual cycle addresses. -** So ignore the whole mess for now. -*/ - - if ((res->flags & (PCI_BASE_ADDRESS_SPACE - | PCI_BASE_ADDRESS_MEM_TYPE_MASK)) - == (PCI_BASE_ADDRESS_SPACE_MEMORY - | PCI_BASE_ADDRESS_MEM_TYPE_64)) { - pci_write_config_dword(dev, where+4, 0); - DBGC("PCIBIOS: dev %s type 64-bit\n", dev->name); - } -} - /* ** Called by pci_set_master() - a driver interface. ** diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index d35111a4bf38..c9b0d0490058 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -93,46 +93,6 @@ fixup_broken_pcnet32(struct pci_dev* dev) } } -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - struct pci_controller* hose = dev->sysdata; - unsigned long io_offset; - - new = res->start; - res->flags &= ~IORESOURCE_UNSET; - if (hose && res->flags & IORESOURCE_IO) { - io_offset = (unsigned long)hose->io_base_virt - isa_io_base; - new -= io_offset; - } - if (hose && res->flags & IORESOURCE_MEM) - new -= hose->pci_mem_offset; - new |= (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } - printk(KERN_INFO "PCI: moved device %s resource %d (%lx) to %x\n", - dev->slot_name, resource, res->flags, - new & ~PCI_REGION_FLAG_MASK); -} - static void pcibios_fixup_resources(struct pci_dev *dev) { diff --git a/arch/ppc64/kernel/pci.c b/arch/ppc64/kernel/pci.c index 7edf23857c24..ba9a6feaeb50 100644 --- a/arch/ppc64/kernel/pci.c +++ b/arch/ppc64/kernel/pci.c @@ -127,37 +127,6 @@ struct pci_dev *pci_find_dev_by_addr(unsigned long addr) return NULL; } -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - struct pci_controller* hose = PCI_GET_PHB_PTR(dev); - - new = res->start; - if (hose && res->flags & IORESOURCE_MEM) - new -= hose->pci_mem_offset; - new |= (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - static void pcibios_fixup_resources(struct pci_dev* dev) { diff --git a/arch/sh/kernel/pcibios.c b/arch/sh/kernel/pcibios.c index 6b785b914ab4..f9bb8ce67b94 100644 --- a/arch/sh/kernel/pcibios.c +++ b/arch/sh/kernel/pcibios.c @@ -6,7 +6,6 @@ * This is GPL'd. * * Provided here are generic versions of: - * pcibios_update_resource() * pcibios_align_resource() * pcibios_enable_device() * pcibios_set_master() @@ -25,34 +24,6 @@ #include #include -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - /* * We need to avoid collisions with `mirrored' VGA ports * and other strange ISA hardware, so we always want the diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 9c1ebf20487c..b6e83af5d8a9 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -854,13 +854,6 @@ char * __init pcibios_setup(char *str) return str; } -/* - */ -void pcibios_update_resource(struct pci_dev *pdev, struct resource *res, - int index) -{ -} - void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 86288ba4e3c2..917614a36c23 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -470,20 +470,10 @@ int pci_assign_resource(struct pci_dev *pdev, int resource) return err; } -void pcibios_update_resource(struct pci_dev *pdev, struct resource *res, - int index) -{ -} - void pcibios_update_irq(struct pci_dev *pdev, int irq) { } -void __devinit pcibios_fixup_pbus_ranges(struct pci_bus *pbus, - struct pbus_set_ranges_data *pranges) -{ -} - void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) { diff --git a/arch/v850/kernel/rte_mb_a_pci.c b/arch/v850/kernel/rte_mb_a_pci.c index 56acfce6b449..8e7c094292f4 100644 --- a/arch/v850/kernel/rte_mb_a_pci.c +++ b/arch/v850/kernel/rte_mb_a_pci.c @@ -287,43 +287,20 @@ void __devinit pcibios_update_irq (struct pci_dev *dev, int irq) pci_write_config_byte (dev, PCI_INTERRUPT_LINE, irq); } -void __nomods_init -pcibios_update_resource (struct pci_dev *dev, struct resource *r, int resource) +void __devinit +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) { - u32 new, check; - int reg; - - if (r->flags & IORESOURCE_IO) - new = (((r->start - MB_A_PCI_IO_ADDR) - & PCI_BASE_ADDRESS_IO_MASK) - | PCI_BASE_ADDRESS_SPACE_IO); - else if (r->flags & IORESOURCE_MEM) - new = (((r->start - MB_A_PCI_MEM_ADDR) - & PCI_BASE_ADDRESS_MEM_MASK) - | PCI_BASE_ADDRESS_MEM_TYPE_32 - | ((r->flags & IORESOURCE_PREFETCH) - ? PCI_BASE_ADDRESS_MEM_PREFETCH - : 0) - | PCI_BASE_ADDRESS_SPACE_MEMORY); - else - panic ("pcibios_update_resource: unknown resource type"); - - if (resource < 6) - reg = PCI_BASE_ADDRESS_0 + 4*resource; - else if (resource == PCI_ROM_RESOURCE) { - r->flags |= PCI_ROM_ADDRESS_ENABLE; - new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else - return; - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk (KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); + unsigned long offset = 0; + + if (res->flags & IORESOURCE_IO) { + offset = MB_A_PCI_IO_ADDR; + } else if (res->flags & IORESOURCE_MEM) { + offset = MB_A_PCI_MEM_ADDR; } + + region->start = res->start - offset; + region->end = res->end - offset; } diff --git a/arch/x86_64/pci/x86-64.c b/arch/x86_64/pci/x86-64.c index f9ee1dd44061..ba5a9f09adac 100644 --- a/arch/x86_64/pci/x86-64.c +++ b/arch/x86_64/pci/x86-64.c @@ -33,34 +33,6 @@ #include "pci.h" -void -pcibios_update_resource(struct pci_dev *dev, struct resource *res, - int resource) -{ - u32 new, check; - int reg; - - new = res->start | (res->flags & PCI_REGION_FLAG_MASK); - if (resource < 6) { - reg = PCI_BASE_ADDRESS_0 + 4*resource; - } else if (resource == PCI_ROM_RESOURCE) { - res->flags |= PCI_ROM_ADDRESS_ENABLE; - new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - pci_write_config_dword(dev, reg, new); - pci_read_config_dword(dev, reg, &check); - if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { - printk(KERN_ERR "PCI: Error while updating region " - "%s/%d (%08x != %08x)\n", dev->slot_name, resource, - new, check); - } -} - /* * We need to avoid collisions with `mirrored' VGA ports * and other strange ISA hardware, so we always want the diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index be89db151d90..4989b88b45d3 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -33,6 +33,59 @@ #endif +static void +pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) +{ + struct pci_bus_region region; + u32 new, check, mask; + int reg; + + pcibios_resource_to_bus(dev, ®ion, res); + + DBGC((KERN_ERR " got res [%lx:%lx] bus [%lx:%lx] for " + "resource %d of %s\n", res->start, res->end, + region.start, region.end, resno, dev->dev.name)); + + new = region.start | (res->flags & PCI_REGION_FLAG_MASK); + if (res->flags & IORESOURCE_IO) + mask = (u32)PCI_BASE_ADDRESS_IO_MASK; + else + mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; + + if (resno < 6) { + reg = PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + new |= res->flags & PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Hmm, non-standard resource. */ + printk("PCI: trying to set non-standard region %s/%d\n", + dev->slot_name, resno); + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + + if ((new ^ check) & mask) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resno, + new, check); + } + + if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) == + (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) { + new = 0; /* currently everyone zeros the high address */ + pci_write_config_dword(dev, reg + 4, new); + pci_read_config_dword(dev, reg + 4, &check); + if (check != new) { + printk(KERN_ERR "PCI: Error updating region " + "%s/%d (high %08x != %08x)\n", + dev->slot_name, resno, new, check); + } + } +} + int __init pci_claim_resource(struct pci_dev *dev, int resource) { @@ -89,10 +142,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n", resno, res->start, res->end, dev->slot_name); } else { - DBGC((KERN_ERR " got res[%lx:%lx] for resource %d of %s\n", - res->start, res->end, resno, dev->dev.name)); - /* Update PCI config space. */ - pcibios_update_resource(dev, res, resno); + pci_update_resource(dev, res, resno); } return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index b01ae2e4df9c..f87c44ae0689 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -529,7 +529,6 @@ char *pcibios_setup (char *str); /* Used only when drivers/pci/setup.c is used */ void pcibios_align_resource(void *, struct resource *, unsigned long, unsigned long); -void pcibios_update_resource(struct pci_dev *, struct resource *, int); void pcibios_update_irq(struct pci_dev *, int irq); /* Generic PCI functions used internally */ -- cgit v1.2.3 From 8ce306e1c6e40f34488a277fbc937e1a9974ae16 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 22:59:17 +0000 Subject: [PCI] pci-10: Miscellaneous cleanups to probe.c Miscellaneous cleanups to probe.c: - make code/comments wrap before column 80. - remove extraneous space. --- drivers/pci/probe.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2715583870db..73509e7635b7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -296,7 +296,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); - DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", dev->slot_name, buses & 0xffffff, pass); + + DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", + dev->slot_name, buses & 0xffffff, pass); + if ((buses & 0xffff00) && !pcibios_assign_all_busses() && !is_cardbus) { unsigned int cmax; /* @@ -358,6 +361,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max child->subordinate = max; pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max); } + sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number); return max; @@ -391,17 +395,20 @@ int pci_setup_device(struct pci_dev * dev) { u32 class; - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - sprintf(dev->dev.name, "PCI device %04x:%04x", dev->vendor, dev->device); + sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->dev.name, "PCI device %04x:%04x", + dev->vendor, dev->device); + INIT_LIST_HEAD(&dev->pools); - + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); class >>= 8; /* upper 3 bytes */ dev->class = class; class >>= 8; - DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, dev->devfn, - dev->vendor, dev->device, class, dev->hdr_type); + DBG("Found %02x:%02x [%04x/%04x] %06x %02x\n", dev->bus->number, + dev->devfn, dev->vendor, dev->device, class, dev->hdr_type); /* "Unknown power state" */ dev->current_state = 4; @@ -468,7 +475,8 @@ pci_scan_device(struct pci_bus *bus, int devfn) return NULL; /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || l == 0x0000ffff || l == 0xffff0000) + if (l == 0xffffffff || l == 0x00000000 || + l == 0x0000ffff || l == 0xffff0000) return NULL; dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); -- cgit v1.2.3 From b65c581a8816d495422bdc098c9438308cba8256 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 23:06:27 +0000 Subject: [PCI] pci-11: use u32 for bus numbers/latency not unsigned long pci_read_config_dword() takes a u32 pointer, not unsigned long. --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 73509e7635b7..56995d565ca9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -291,9 +291,9 @@ static unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus); */ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max, int pass) { - unsigned int buses; struct pci_bus *child; int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS); + u32 buses; pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses); -- cgit v1.2.3 From c907ed24d7f3d5778a4b204713bed1525c65157a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 23:10:44 +0000 Subject: [PCI] pci-12: Add #defines for cardbus specifics Pull out the bits of cardbus configuration - the secondary latency timer, and the number of bus numbers we reserve. --- drivers/pci/probe.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 56995d565ca9..f07cb365cf82 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,9 @@ #define DBG(x...) #endif +#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ +#define CARDBUS_RESERVE_BUSNR 3 + LIST_HEAD(pci_root_buses); LIST_HEAD(pci_devices); @@ -336,8 +339,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max * yenta.c forces a secondary latency timer of 176. * Copy that behaviour here. */ - if (is_cardbus) - buses = (buses & 0x00ffffff) | (176 << 24); + if (is_cardbus) { + buses &= ~0xff000000; + buses |= CARDBUS_LATENCY_TIMER << 24; + } /* * We need to blast all three values with a single write. @@ -353,7 +358,7 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev * dev, int max * as cards with a PCI-to-PCI bridge can be * inserted later. */ - max += 3; + max += CARDBUS_RESERVE_BUSNR; } /* * Set the subordinate bus number to its real value. -- cgit v1.2.3 From 52e13cc28ad2893e604650ccd72ddd0ea5fad509 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 23:21:31 +0000 Subject: [PCI] pci-13: unuse pci_do_scan_bus() In an attempt to "unuse" pci_do_scan_bus() so it can be eventually killed, make pci_scan_bus_parented() call the new pci_scan_child_bus() and pci_bus_add_devices(). The only remaining callers are the hotplug drivers. Eventually, pci_bus_add_devices() will be removed from this function - it is intended that architectures should call this after they have done any setups and fixups to the scanned bus. It is legal to call pci_bus_add_devices() on a bus which has already had this function called, so architectures could update today. --- drivers/pci/probe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f07cb365cf82..88b30de4726a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -663,7 +663,8 @@ struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent, int bus, if (b) { b->sysdata = sysdata; b->ops = ops; - b->subordinate = pci_do_scan_bus(b); + b->subordinate = pci_scan_child_bus(b); + pci_bus_add_devices(b); } return b; } -- cgit v1.2.3 From fc8b54a1a79a1c9795cbe8a2629b2e67fb7f73e6 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 23:24:45 +0000 Subject: [PCI] pci-14: Add the Mobility Electronics EV1000 PCI device numbers. --- drivers/pci/pci.ids | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci.ids b/drivers/pci/pci.ids index 60d3a51f93a3..0ee6b158705c 100644 --- a/drivers/pci/pci.ids +++ b/drivers/pci/pci.ids @@ -5440,6 +5440,11 @@ 14f1 2004 Dynalink 56PMi 8234 RS8234 ATM SAR Controller [ServiceSAR Plus] 14f2 MOBILITY Electronics + 0120 EV1000 bridge + 0121 EV1000 Parallel port + 0122 EV1000 Serial port + 0123 EV1000 Keyboard controller + 0124 EV1000 Mouse controller 14f3 BROADLOGIC 14f4 TOKYO Electronic Industry CO Ltd 14f5 SOPAC Ltd -- cgit v1.2.3 From 18e35894a4d4661a81e47176b296f24c5e722d6f Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 16 Mar 2003 23:28:51 +0000 Subject: [PCI] pci-15: Fix setup-bus.c resource sizing. Patch from Ivan Kokshaysky This fixes long standing typo ('size' instead of 'r_size') which causes overestimate of the bridge memory ranges calculated in pbus_size_mem(). For example, if we have a device with one 1Mb and one 2Mb memory ranges behind the bridge, calculated size and alignment of the bridge memory window will be 4Mb and 2Mb respectively, while the correct values are 3Mb and 1Mb. --- drivers/pci/setup-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 0eab0b57e10a..172174f9c61e 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -275,7 +275,7 @@ pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) order = 0; /* Exclude ranges with size > align from calculation of the alignment. */ - if (size == align) + if (r_size == align) aligns[order] += align; if (order > max_order) max_order = order; -- cgit v1.2.3 From 5577ba7d60583d2ee4426c80d90b47197122735c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 16 Mar 2003 07:22:39 -0800 Subject: [PATCH] Fix memleak in e100 driver Patch from Oleg Drokin There is a memleak in e100 driver from intel, both in 2.4 and 2.5 e100_ethtool_gstrings does not free "strings" variable if it cannot copy it to userspace. --- drivers/net/e100/e100_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e100/e100_main.c b/drivers/net/e100/e100_main.c index 523e83f771da..1f1f70caca21 100644 --- a/drivers/net/e100/e100_main.c +++ b/drivers/net/e100/e100_main.c @@ -3784,11 +3784,15 @@ static int e100_ethtool_gstrings(struct net_device *dev, struct ifreq *ifr) return -EOPNOTSUPP; } - if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) { + kfree(strings); return -EFAULT; + } - if (copy_to_user(usr_strings, strings, info.len * ETH_GSTRING_LEN)) + if (copy_to_user(usr_strings, strings, info.len * ETH_GSTRING_LEN)) { + kfree(strings); return -EFAULT; + } kfree(strings); return 0; -- cgit v1.2.3 From c96c506f82924d63feedfd1e369b05cd01a26f10 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 16 Mar 2003 07:23:25 -0800 Subject: [PATCH] fix raid0 oops raid0 doesn't have a thread, so md_wakeup_thread() derefs NULL. Neil may end up doing this differently, but meanwhile.... --- drivers/md/md.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/md/md.c b/drivers/md/md.c index b814b0aefd5b..cefd2423f1c7 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2818,9 +2818,11 @@ int md_thread(void * arg) void md_wakeup_thread(mdk_thread_t *thread) { - dprintk("md: waking up MD thread %p.\n", thread); - set_bit(THREAD_WAKEUP, &thread->flags); - wake_up(&thread->wqueue); + if (thread) { + dprintk("md: waking up MD thread %p.\n", thread); + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); + } } mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, -- cgit v1.2.3 From 5ebe2675fad72d7f5e92ffbd69e2e3a118b925e4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 16 Mar 2003 07:23:33 -0800 Subject: [PATCH] miropcm20-rds.c compile fixes Patch from Adrian Bunk It would be nice if everyone would try to compile the patched files before submitting patches... --- drivers/media/radio/miropcm20-rds.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/media/radio/miropcm20-rds.c b/drivers/media/radio/miropcm20-rds.c index fa2eebe2ecd0..dd08c51cae80 100644 --- a/drivers/media/radio/miropcm20-rds.c +++ b/drivers/media/radio/miropcm20-rds.c @@ -113,7 +113,7 @@ static struct file_operations rds_fops = { static struct miscdevice rds_miscdev = { .minor = MISC_DYNAMIC_MINOR, - .name = "radiotext" + .name = "radiotext", .fops = &rds_fops, }; @@ -128,7 +128,7 @@ static int __init miropcm20_rds_init(void) error = devfs_mk_symlink(NULL, "v4l/rds/radiotext", 0, "../misc/radiotext", NULL, NULL); if (error) - misc_deregister(&rds_miscdev) + misc_deregister(&rds_miscdev); return error; } @@ -136,7 +136,7 @@ static int __init miropcm20_rds_init(void) static void __exit miropcm20_rds_cleanup(void) { devfs_remove("v4l/rds/radiotext"); - misc_deregister(&rds_miscdev) + misc_deregister(&rds_miscdev); } module_init(miropcm20_rds_init); -- cgit v1.2.3 From cae0dc9c3ce94e954c6f973cd91124355b5b8b38 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 16 Mar 2003 16:20:50 -0800 Subject: [PATCH] honor hard barrier in deadline Previously, we only honored barriers wrt merging. Really honor them now, move all pending requests to dispatch list and add the hard barrier at the back. --- drivers/block/deadline-iosched.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c index a107ec9682ce..6735d56e2264 100644 --- a/drivers/block/deadline-iosched.c +++ b/drivers/block/deadline-iosched.c @@ -608,6 +608,12 @@ deadline_insert_request(request_queue_t *q, struct request *rq, if (unlikely(rq->flags & REQ_HARDBARRIER)) { DL_INVALIDATE_HASH(dd); q->last_merge = NULL; + + while (deadline_dispatch_requests(dd)) + ; + + list_add_tail(&rq->queuelist, dd->dispatch); + return; } if (unlikely(!blk_fs_request(rq))) { -- cgit v1.2.3 From 0d61cac67ad899bbb330c59fd4224b739ab8f9c1 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 17 Mar 2003 21:13:31 -0800 Subject: [PATCH] remove unused block congestion code Patch from: Hugh Dickins Removes a ton of dead code from ll_rw_blk.c. I don't expect we'll be using this now. --- drivers/block/ll_rw_blk.c | 65 ++++++++------------------------------------- include/linux/backing-dev.h | 12 --------- 2 files changed, 11 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 9d64f8cc690d..c86baf21c2b2 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -56,11 +56,7 @@ static int batch_requests; unsigned long blk_max_low_pfn, blk_max_pfn; int blk_nohighio = 0; -static struct congestion_state { - wait_queue_head_t wqh; - atomic_t nr_congested_queues; - atomic_t nr_active_queues; -} congestion_states[2]; +static wait_queue_head_t congestion_wqh[2]; /* * Return the threshold (number of free requests) at which the queue is @@ -98,14 +94,12 @@ static inline int queue_congestion_off_threshold(void) static void clear_queue_congested(request_queue_t *q, int rw) { enum bdi_state bit; - struct congestion_state *cs = &congestion_states[rw]; + wait_queue_head_t *wqh = &congestion_wqh[rw]; bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested; - - if (test_and_clear_bit(bit, &q->backing_dev_info.state)) - atomic_dec(&cs->nr_congested_queues); - if (waitqueue_active(&cs->wqh)) - wake_up(&cs->wqh); + clear_bit(bit, &q->backing_dev_info.state); + if (waitqueue_active(wqh)) + wake_up(wqh); } /* @@ -117,37 +111,7 @@ static void set_queue_congested(request_queue_t *q, int rw) enum bdi_state bit; bit = (rw == WRITE) ? BDI_write_congested : BDI_read_congested; - - if (!test_and_set_bit(bit, &q->backing_dev_info.state)) - atomic_inc(&congestion_states[rw].nr_congested_queues); -} - -/* - * A queue has just put back its last read or write request and has fallen - * idle. - */ -static void clear_queue_active(request_queue_t *q, int rw) -{ - enum bdi_state bit; - - bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; - - if (test_and_clear_bit(bit, &q->backing_dev_info.state)) - atomic_dec(&congestion_states[rw].nr_active_queues); -} - -/* - * A queue has just taken its first read or write request and has become - * active. - */ -static void set_queue_active(request_queue_t *q, int rw) -{ - enum bdi_state bit; - - bit = (rw == WRITE) ? BDI_write_active : BDI_read_active; - - if (!test_and_set_bit(bit, &q->backing_dev_info.state)) - atomic_inc(&congestion_states[rw].nr_active_queues); + set_bit(bit, &q->backing_dev_info.state); } /** @@ -1325,8 +1289,6 @@ static struct request *get_request(request_queue_t *q, int rw) rq = blkdev_free_rq(&rl->free); list_del_init(&rq->queuelist); rq->ref_count = 1; - if (rl->count == queue_nr_requests) - set_queue_active(q, rw); rl->count--; if (rl->count < queue_congestion_on_threshold()) set_queue_congested(q, rw); @@ -1569,8 +1531,6 @@ void __blk_put_request(request_queue_t *q, struct request *req) rl->count++; if (rl->count >= queue_congestion_off_threshold()) clear_queue_congested(q, rw); - if (rl->count == queue_nr_requests) - clear_queue_active(q, rw); if (rl->count >= batch_requests && waitqueue_active(&rl->wait)) wake_up(&rl->wait); } @@ -1605,12 +1565,12 @@ void blk_put_request(struct request *req) void blk_congestion_wait(int rw, long timeout) { DEFINE_WAIT(wait); - struct congestion_state *cs = &congestion_states[rw]; + wait_queue_head_t *wqh = &congestion_wqh[rw]; blk_run_queues(); - prepare_to_wait(&cs->wqh, &wait, TASK_UNINTERRUPTIBLE); + prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE); io_schedule_timeout(timeout); - finish_wait(&cs->wqh, &wait); + finish_wait(wqh, &wait); } /* @@ -2249,11 +2209,8 @@ int __init blk_dev_init(void) blk_max_low_pfn = max_low_pfn; blk_max_pfn = max_pfn; - for (i = 0; i < ARRAY_SIZE(congestion_states); i++) { - init_waitqueue_head(&congestion_states[i].wqh); - atomic_set(&congestion_states[i].nr_congested_queues, 0); - atomic_set(&congestion_states[i].nr_active_queues, 0); - } + for (i = 0; i < ARRAY_SIZE(congestion_wqh); i++) + init_waitqueue_head(&congestion_wqh[i]); return 0; }; diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 55218964e7ef..94c93c9c5f66 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -17,8 +17,6 @@ enum bdi_state { BDI_pdflush, /* A pdflush thread is working this device */ BDI_write_congested, /* The write queue is getting full */ BDI_read_congested, /* The read queue is getting full */ - BDI_write_active, /* There are one or more queued writes */ - BDI_read_active, /* There are one or more queued reads */ BDI_unused, /* Available bits start here */ }; @@ -44,14 +42,4 @@ static inline int bdi_write_congested(struct backing_dev_info *bdi) return test_bit(BDI_write_congested, &bdi->state); } -static inline int bdi_read_active(struct backing_dev_info *bdi) -{ - return test_bit(BDI_read_active, &bdi->state); -} - -static inline int bdi_write_active(struct backing_dev_info *bdi) -{ - return test_bit(BDI_write_active, &bdi->state); -} - #endif /* _LINUX_BACKING_DEV_H */ -- cgit v1.2.3 From ca36489611eb2b5f6f79df2262e8862041801fc2 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 17 Mar 2003 21:15:26 -0800 Subject: [PATCH] file->f_list locking in tty_io.c release_mem() is altering the file->f_list lists without taking the appropriate spinlock. --- drivers/char/tty_io.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 86e3216d0547..c712200fd2ea 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1034,7 +1034,9 @@ static void release_mem(struct tty_struct *tty, int idx) } o_tty->magic = 0; (*o_tty->driver.refcount)--; + file_list_lock(); list_del(&o_tty->tty_files); + file_list_unlock(); free_tty_struct(o_tty); } @@ -1046,7 +1048,9 @@ static void release_mem(struct tty_struct *tty, int idx) } tty->magic = 0; (*tty->driver.refcount)--; + file_list_lock(); list_del(&tty->tty_files); + file_list_unlock(); module_put(tty->driver.owner); free_tty_struct(tty); } -- cgit v1.2.3 From cb99514855944beefe61b2ee095f3c1227f65ced Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 17 Mar 2003 21:17:22 -0800 Subject: [PATCH] stack reduction in drivers/char/vt_ioctl.c Patch from "Randy.Dunlap" This patch (to 2.5.64) reduces the stack usage in vt_ioctl() from 0x334 bytes to 0xec bytes (P4, UP, gcc 2.96). --- drivers/char/vt_ioctl.c | 103 ++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index cda2f20d8308..b29ecd8bca5f 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -191,38 +191,56 @@ do_kbkeycode_ioctl(int cmd, struct kbkeycode *user_kbkc, int perm) static inline int do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) { - struct kbsentry tmp; + struct kbsentry *kbs; char *p; u_char *q; int sz; int delta; char *first_free, *fj, *fnw; int i, j, k; + int ret; + + kbs = kmalloc(sizeof(*kbs), GFP_KERNEL); + if (!kbs) { + ret = -ENOMEM; + goto reterr; + } /* we mostly copy too much here (512bytes), but who cares ;) */ - if (copy_from_user(&tmp, user_kdgkb, sizeof(struct kbsentry))) - return -EFAULT; - tmp.kb_string[sizeof(tmp.kb_string)-1] = '\0'; - if (tmp.kb_func >= MAX_NR_FUNC) - return -EINVAL; - i = tmp.kb_func; + if (copy_from_user(kbs, user_kdgkb, sizeof(struct kbsentry))) { + ret = -EFAULT; + goto reterr; + } + kbs->kb_string[sizeof(kbs->kb_string)-1] = '\0'; + if (kbs->kb_func >= MAX_NR_FUNC) { + ret = -EINVAL; + goto reterr; + } + i = kbs->kb_func; switch (cmd) { case KDGKBSENT: - sz = sizeof(tmp.kb_string) - 1; /* sz should have been + sz = sizeof(kbs->kb_string) - 1; /* sz should have been a struct member */ q = user_kdgkb->kb_string; p = func_table[i]; if(p) for ( ; *p && sz; p++, sz--) - if (put_user(*p, q++)) - return -EFAULT; - if (put_user('\0', q)) - return -EFAULT; + if (put_user(*p, q++)) { + ret = -EFAULT; + goto reterr; + } + if (put_user('\0', q)) { + ret = -EFAULT; + goto reterr; + } + kfree(kbs); return ((p && *p) ? -EOVERFLOW : 0); case KDSKBSENT: - if (!perm) - return -EPERM; + if (!perm) { + ret = -EPERM; + goto reterr; + } q = func_table[i]; first_free = funcbufptr + (funcbufsize - funcbufleft); @@ -233,7 +251,7 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) else fj = first_free; - delta = (q ? -strlen(q) : 1) + strlen(tmp.kb_string); + delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string); if (delta <= funcbufleft) { /* it fits in current buf */ if (j < MAX_NR_FUNC) { memmove(fj + delta, fj, first_free - fj); @@ -249,8 +267,10 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) while (sz < funcbufsize - funcbufleft + delta) sz <<= 1; fnw = (char *) kmalloc(sz, GFP_KERNEL); - if(!fnw) - return -ENOMEM; + if(!fnw) { + ret = -ENOMEM; + goto reterr; + } if (!q) func_table[i] = fj; @@ -272,17 +292,19 @@ do_kdgkb_ioctl(int cmd, struct kbsentry *user_kdgkb, int perm) funcbufleft = funcbufleft - delta + sz - funcbufsize; funcbufsize = sz; } - strcpy(func_table[i], tmp.kb_string); + strcpy(func_table[i], kbs->kb_string); break; } - return 0; + ret = 0; +reterr: + kfree(kbs); + return ret; } static inline int -do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm) +do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm, struct console_font_op *op) { struct consolefontdesc cfdarg; - struct console_font_op op; int i; if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc))) @@ -292,25 +314,25 @@ do_fontx_ioctl(int cmd, struct consolefontdesc *user_cfd, int perm) case PIO_FONTX: if (!perm) return -EPERM; - op.op = KD_FONT_OP_SET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = cfdarg.charheight; - op.charcount = cfdarg.charcount; - op.data = cfdarg.chardata; - return con_font_op(fg_console, &op); + op->op = KD_FONT_OP_SET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; + return con_font_op(fg_console, op); case GIO_FONTX: { - op.op = KD_FONT_OP_GET; - op.flags = KD_FONT_FLAG_OLD; - op.width = 8; - op.height = cfdarg.charheight; - op.charcount = cfdarg.charcount; - op.data = cfdarg.chardata; - i = con_font_op(fg_console, &op); + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; + i = con_font_op(fg_console, op); if (i) return i; - cfdarg.charheight = op.height; - cfdarg.charcount = op.charcount; + cfdarg.charheight = op->height; + cfdarg.charcount = op->charcount; if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) return -EFAULT; return 0; @@ -355,6 +377,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, unsigned char ucval; struct kbd_struct * kbd; struct vt_struct *vt = (struct vt_struct *)tty->driver_data; + struct console_font_op op; /* used in multiple places here */ console = vt->vc_num; @@ -860,7 +883,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case PIO_FONT: { - struct console_font_op op; if (!perm) return -EPERM; op.op = KD_FONT_OP_SET; @@ -873,7 +895,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case GIO_FONT: { - struct console_font_op op; op.op = KD_FONT_OP_GET; op.flags = KD_FONT_FLAG_OLD; op.width = 8; @@ -893,7 +914,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, case PIO_FONTX: case GIO_FONTX: - return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm); + return do_fontx_ioctl(cmd, (struct consolefontdesc *)arg, perm, &op); case PIO_FONTRESET: { @@ -906,7 +927,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, return -ENOSYS; #else { - struct console_font_op op; op.op = KD_FONT_OP_SET_DEFAULT; op.data = NULL; i = con_font_op(fg_console, &op); @@ -918,7 +938,6 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } case KDFONTOP: { - struct console_font_op op; if (copy_from_user(&op, (void *) arg, sizeof(op))) return -EFAULT; if (!perm && op.op != KD_FONT_OP_GET) -- cgit v1.2.3 From d6ff057c0f40035b158fa7106531eb4d95920329 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 17 Mar 2003 21:19:50 -0800 Subject: [PATCH] fix oprofile timer race Patch from John Levon wli got an oops from this. The callbacks call mod_timer so the timer had better be setup by then --- drivers/oprofile/buffer_sync.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index c2b0b6a833a2..9162f38d8395 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -82,9 +82,16 @@ static struct notifier_block exit_mmap_nb = { int sync_start(void) { - int err = profile_event_register(EXIT_TASK, &exit_task_nb); + int err; + + init_timer(&sync_timer); + sync_timer.function = timer_ping; + sync_timer.expires = jiffies + DEFAULT_EXPIRE; + add_timer(&sync_timer); + + err = profile_event_register(EXIT_TASK, &exit_task_nb); if (err) - goto out; + goto out1; err = profile_event_register(EXIT_MMAP, &exit_mmap_nb); if (err) goto out2; @@ -92,16 +99,14 @@ int sync_start(void) if (err) goto out3; - init_timer(&sync_timer); - sync_timer.function = timer_ping; - sync_timer.expires = jiffies + DEFAULT_EXPIRE; - add_timer(&sync_timer); out: return err; out3: profile_event_unregister(EXIT_MMAP, &exit_mmap_nb); out2: profile_event_unregister(EXIT_TASK, &exit_task_nb); +out1: + del_timer_sync(&sync_timer); goto out; } -- cgit v1.2.3 From d99c48c60bc52561b3125dd1f85db7a77b7031ca Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 17 Mar 2003 21:20:37 -0800 Subject: [PATCH] Add error checking get_disk() Patch from Bob Miller The get_disk() function should check the return value from kobject_get() before passing it to to_disk(). This patch fixes this error. (Acked by Pat) --- drivers/block/genhd.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 04d9cc8a48b5..9812dcf39fe8 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -538,12 +538,20 @@ struct gendisk *alloc_disk(int minors) struct gendisk *get_disk(struct gendisk *disk) { struct module *owner; + struct kobject *kobj; + if (!disk->fops) return NULL; owner = disk->fops->owner; if (owner && !try_module_get(owner)) return NULL; - return to_disk(kobject_get(&disk->kobj)); + kobj = kobject_get(&disk->kobj); + if (kobj == NULL) { + module_put(owner); + return NULL; + } + return to_disk(kobj); + } void put_disk(struct gendisk *disk) -- cgit v1.2.3