diff options
Diffstat (limited to 'drivers')
66 files changed, 4711 insertions, 2406 deletions
diff --git a/drivers/block/cfq-iosched.c b/drivers/block/cfq-iosched.c index ff1cc968f96..de5746e38af 100644 --- a/drivers/block/cfq-iosched.c +++ b/drivers/block/cfq-iosched.c @@ -300,7 +300,6 @@ CFQ_CRQ_FNS(requeued); static struct cfq_queue *cfq_find_cfq_hash(struct cfq_data *, unsigned int, unsigned short); static void cfq_dispatch_sort(request_queue_t *, struct cfq_rq *); static void cfq_put_cfqd(struct cfq_data *cfqd); -static inline int cfq_pending_requests(struct cfq_data *cfqd); #define process_sync(tsk) ((tsk)->flags & PF_SYNCWRITE) @@ -348,6 +347,28 @@ static struct request *cfq_find_rq_hash(struct cfq_data *cfqd, sector_t offset) return NULL; } +static inline int cfq_pending_requests(struct cfq_data *cfqd) +{ + return !list_empty(&cfqd->queue->queue_head) || cfqd->busy_queues; +} + +/* + * scheduler run of queue, if there are requests pending and no one in the + * driver that will restart queueing + */ +static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) +{ + if (!cfqd->rq_in_driver && cfq_pending_requests(cfqd)) + kblockd_schedule_work(&cfqd->unplug_work); +} + +static int cfq_queue_empty(request_queue_t *q) +{ + struct cfq_data *cfqd = q->elevator->elevator_data; + + return !cfq_pending_requests(cfqd); +} + /* * Lifted from AS - choose which of crq1 and crq2 that is best served now. * We choose the request that is closest to the head right now. Distance @@ -1072,16 +1093,6 @@ cfq_prio_to_maxrq(struct cfq_data *cfqd, struct cfq_queue *cfqq) } /* - * scheduler run of queue, if there are requests pending and no one in the - * driver that will restart queueing - */ -static inline void cfq_schedule_dispatch(struct cfq_data *cfqd) -{ - if (!cfqd->rq_in_driver && cfq_pending_requests(cfqd)) - kblockd_schedule_work(&cfqd->unplug_work); -} - -/* * get next queue for service */ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd, int force) @@ -1846,18 +1857,6 @@ cfq_insert_request(request_queue_t *q, struct request *rq, int where) } } -static inline int cfq_pending_requests(struct cfq_data *cfqd) -{ - return !list_empty(&cfqd->queue->queue_head) || cfqd->busy_queues; -} - -static int cfq_queue_empty(request_queue_t *q) -{ - struct cfq_data *cfqd = q->elevator->elevator_data; - - return !cfq_pending_requests(cfqd); -} - static void cfq_completed_request(request_queue_t *q, struct request *rq) { struct cfq_rq *crq = RQ_DATA(rq); @@ -1952,7 +1951,7 @@ __cfq_may_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq, { #if 1 if ((cfq_cfqq_wait_request(cfqq) || cfq_cfqq_must_alloc(cfqq)) && - !cfq_cfqq_must_alloc_slice) { + !cfq_cfqq_must_alloc_slice(cfqq)) { cfq_mark_cfqq_must_alloc_slice(cfqq); return ELV_MQUEUE_MUST; } @@ -1969,7 +1968,7 @@ __cfq_may_queue(struct cfq_data *cfqd, struct cfq_queue *cfqq, * only allow 1 ELV_MQUEUE_MUST per slice, otherwise we * can quickly flood the queue with writes from a single task */ - if (rw == READ || !cfq_cfqq_must_alloc_slice) { + if (rw == READ || !cfq_cfqq_must_alloc_slice(cfqq)) { cfq_mark_cfqq_must_alloc_slice(cfqq); return ELV_MQUEUE_MUST; } diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index e481cc411b5..5ef9adb9fe7 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -1089,6 +1089,14 @@ static int bluecard_event(event_t event, int priority, event_callback_args_t *ar return 0; } +static struct pcmcia_device_id bluecard_ids[] = { + PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e), + PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c), + PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, bluecard_ids); + static struct pcmcia_driver bluecard_driver = { .owner = THIS_MODULE, .drv = { @@ -1096,6 +1104,7 @@ static struct pcmcia_driver bluecard_driver = { }, .attach = bluecard_attach, .detach = bluecard_detach, + .id_table = bluecard_ids, }; static int __init init_bluecard_cs(void) diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index f71e5c76963..9013cd759af 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -935,6 +935,12 @@ static int bt3c_event(event_t event, int priority, event_callback_args_t *args) return 0; } +static struct pcmcia_device_id bt3c_ids[] = { + PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, bt3c_ids); + static struct pcmcia_driver bt3c_driver = { .owner = THIS_MODULE, .drv = { @@ -942,6 +948,7 @@ static struct pcmcia_driver bt3c_driver = { }, .attach = bt3c_attach, .detach = bt3c_detach, + .id_table = bt3c_ids, }; static int __init init_bt3c_cs(void) diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index ad8d972444a..c479484a1f7 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -855,6 +855,12 @@ static int btuart_event(event_t event, int priority, event_callback_args_t *args return 0; } +static struct pcmcia_device_id btuart_ids[] = { + /* don't use this driver. Use serial_cs + hci_uart instead */ + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, btuart_ids); + static struct pcmcia_driver btuart_driver = { .owner = THIS_MODULE, .drv = { @@ -862,6 +868,7 @@ static struct pcmcia_driver btuart_driver = { }, .attach = btuart_attach, .detach = btuart_detach, + .id_table = btuart_ids, }; static int __init init_btuart_cs(void) diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index fe954e5d9a1..bb12f7daeb9 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -807,6 +807,13 @@ static int dtl1_event(event_t event, int priority, event_callback_args_t *args) return 0; } +static struct pcmcia_device_id dtl1_ids[] = { + PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d), + PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, dtl1_ids); + static struct pcmcia_driver dtl1_driver = { .owner = THIS_MODULE, .drv = { @@ -814,6 +821,7 @@ static struct pcmcia_driver dtl1_driver = { }, .attach = dtl1_attach, .detach = dtl1_detach, + .id_table = dtl1_ids, }; static int __init init_dtl1_cs(void) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 1c8d866a49d..8f36b1758eb 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -581,7 +581,7 @@ static dev_link_t *mgslpc_attach(void) /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; link->conf.Attributes = 0; @@ -3081,6 +3081,12 @@ void mgslpc_remove_device(MGSLPC_INFO *remove_info) } } +static struct pcmcia_device_id mgslpc_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x02c5, 0x0050), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, mgslpc_ids); + static struct pcmcia_driver mgslpc_driver = { .owner = THIS_MODULE, .drv = { @@ -3088,6 +3094,7 @@ static struct pcmcia_driver mgslpc_driver = { }, .attach = mgslpc_attach, .detach = mgslpc_detach, + .id_table = mgslpc_ids, }; static struct tty_operations mgslpc_ops = { diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index 0273f124a4f..5f33df47aa7 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -606,6 +606,12 @@ config BLK_DEV_IT8172 <http://www.ite.com.tw/ia/brief_it8172bsp.htm>; picture of the board at <http://www.mvista.com/partners/semiconductor/ite.html>. +config BLK_DEV_IT821X + tristate "IT821X IDE support" + help + This driver adds support for the ITE 8211 IDE controller and the + IT 8212 IDE RAID controller in both RAID and pass-through mode. + config BLK_DEV_NS87415 tristate "NS87415 chipset support" help diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index d6f934886b0..f9c1acb4ed6 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -119,6 +119,10 @@ static int lba_capacity_is_ok (struct hd_driveid *id) { unsigned long lba_sects, chs_sects, head, tail; + /* No non-LBA info .. so valid! */ + if (id->cyls == 0) + return 1; + /* * The ATA spec tells large drives to return * C/H/S = 16383/16/63 independent of their size. diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 2d2eefb610d..1e1531334c2 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -132,7 +132,6 @@ static const struct drive_list_entry drive_blacklist [] = { { "SAMSUNG CD-ROM SC-148C", "ALL" }, { "SAMSUNG CD-ROM SC", "ALL" }, { "SanDisk SDP3B-64" , "ALL" }, - { "SAMSUNG CD-ROM SN-124", "ALL" }, { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, { "_NEC DV5800A", "ALL" }, { NULL , NULL } diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index 53024942a7e..b443b04a4c5 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -1181,7 +1181,8 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) pre_reset(drive); SELECT_DRIVE(drive); udelay (20); - hwif->OUTB(WIN_SRST, IDE_COMMAND_REG); + hwif->OUTBSYNC(drive, WIN_SRST, IDE_COMMAND_REG); + ndelay(400); hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; hwgroup->polling = 1; __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20, NULL); diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index e20327e54b1..978d27d6452 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -457,6 +457,40 @@ int ide_event(event_t event, int priority, return 0; } /* ide_event */ +static struct pcmcia_device_id ide_ids[] = { + PCMCIA_DEVICE_FUNC_ID(4), + PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704), + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d), + PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), + PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0), + PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74), + PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4), + PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde), + PCMCIA_DEVICE_PROD_ID12("EXP", "CD", 0x6f58c983, 0xaae5994f), + PCMCIA_DEVICE_PROD_ID12("EXP ", "CD-ROM", 0x0a5c52fd, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("EXP ", "PnPIDE", 0x0a5c52fd, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e), + PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2 ", 0x547e66dc, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674), + PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12(" ", "NinjaATA-", 0x3b6e20c8, 0xebe0bd79), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1), + PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), + PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), + PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, ide_ids); + static struct pcmcia_driver ide_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -464,6 +498,7 @@ static struct pcmcia_driver ide_cs_driver = { }, .attach = ide_attach, .detach = ide_detach, + .id_table = ide_ids, }; static int __init init_ide_cs(void) diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile index 55e6e553e49..af46226c179 100644 --- a/drivers/ide/pci/Makefile +++ b/drivers/ide/pci/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o #obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o +obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index 4565cc311ff..da46577380f 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -39,6 +39,17 @@ #include <asm/io.h> +static int ide_generic_all; /* Set to claim all devices */ + +static int __init ide_generic_all_on(char *unused) +{ + ide_generic_all = 1; + printk(KERN_INFO "IDE generic will claim all unknown PCI IDE storage controllers.\n"); + return 1; +} + +__setup("all-generic-ide", ide_generic_all_on); + static void __devinit init_hwif_generic (ide_hwif_t *hwif) { switch(hwif->pci_dev->device) { @@ -78,79 +89,85 @@ static void __devinit init_hwif_generic (ide_hwif_t *hwif) static ide_pci_device_t generic_chipsets[] __devinitdata = { { /* 0 */ + .name = "Unknown", + .init_hwif = init_hwif_generic, + .channels = 2, + .autodma = AUTODMA, + .bootable = ON_BOARD, + },{ /* 1 */ .name = "NS87410", .init_hwif = init_hwif_generic, .channels = 2, .autodma = AUTODMA, .enablebits = {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, .bootable = ON_BOARD, - },{ /* 1 */ + },{ /* 2 */ .name = "SAMURAI", .init_hwif = init_hwif_generic, .channels = 2, .autodma = AUTODMA, .bootable = ON_BOARD, - },{ /* 2 */ + },{ /* 3 */ .name = "HT6565", .init_hwif = init_hwif_generic, .channels = 2, .autodma = AUTODMA, .bootable = ON_BOARD, - },{ /* 3 */ + },{ /* 4 */ .name = "UM8673F", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NODMA, .bootable = ON_BOARD, - },{ /* 4 */ + },{ /* 5 */ .name = "UM8886A", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NODMA, .bootable = ON_BOARD, - },{ /* 5 */ + },{ /* 6 */ .name = "UM8886BF", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NODMA, .bootable = ON_BOARD, - },{ /* 6 */ + },{ /* 7 */ .name = "HINT_IDE", .init_hwif = init_hwif_generic, .channels = 2, .autodma = AUTODMA, .bootable = ON_BOARD, - },{ /* 7 */ + },{ /* 8 */ .name = "VIA_IDE", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NOAUTODMA, .bootable = ON_BOARD, - },{ /* 8 */ + },{ /* 9 */ .name = "OPTI621V", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NOAUTODMA, .bootable = ON_BOARD, - },{ /* 9 */ + },{ /* 10 */ .name = "VIA8237SATA", .init_hwif = init_hwif_generic, .channels = 2, .autodma = AUTODMA, .bootable = OFF_BOARD, - },{ /* 10 */ + },{ /* 11 */ .name = "Piccolo0102", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NOAUTODMA, .bootable = ON_BOARD, - },{ /* 11 */ + },{ /* 12 */ .name = "Piccolo0103", .init_hwif = init_hwif_generic, .channels = 2, .autodma = NOAUTODMA, .bootable = ON_BOARD, - },{ /* 12 */ + },{ /* 13 */ .name = "Piccolo0105", .init_hwif = init_hwif_generic, .channels = 2, @@ -174,6 +191,10 @@ static int __devinit generic_init_one(struct pci_dev *dev, const struct pci_devi u16 command; int ret = -ENODEV; + /* Don't use the generic entry unless instructed to do so */ + if (id->driver_data == 0 && ide_generic_all == 0) + goto out; + if (dev->vendor == PCI_VENDOR_ID_UMC && dev->device == PCI_DEVICE_ID_UMC_UM8886A && (!(PCI_FUNC(dev->devfn) & 1))) @@ -195,21 +216,23 @@ out: } static struct pci_device_id generic_pci_tbl[] = { - { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, - { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, - { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, - { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, - { PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, - { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, + { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, + { PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, + { PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, + { PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, + { PCI_VENDOR_ID_HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, + { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, #ifdef CONFIG_BLK_DEV_IDE_SATA - { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, + { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, #endif - { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, - { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, - { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12}, + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12}, + { PCI_VENDOR_ID_TOSHIBA,PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13}, + /* Must come last. If you add entries adjust this table appropriately and the init_one code */ + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 0}, { 0, }, }; MODULE_DEVICE_TABLE(pci, generic_pci_tbl); diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index c8ee0b8c029..7b64db10d1b 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -10,6 +10,11 @@ * donation of an ABit BP6 mainboard, processor, and memory acellerated * development and support. * + * + * Highpoint have their own driver (source except for the raid part) + * available from http://www.highpoint-tech.com/hpt3xx-opensource-v131.tgz + * This may be useful to anyone wanting to work on the mainstream hpt IDE. + * * Note that final HPT370 support was done by force extraction of GPL. * * - add function for getting/setting power status of drive @@ -446,44 +451,29 @@ static struct chipset_bus_clock_list_entry sixty_six_base_hpt374[] = { #define F_LOW_PCI_50 0x2d #define F_LOW_PCI_66 0x42 -/* FIXME: compare with driver's code before removing */ -#if 0 - if (hpt_minimum_revision(dev, 3)) { - u8 cbl; - cbl = inb(iobase + 0x7b); - outb(cbl | 1, iobase + 0x7b); - outb(cbl & ~1, iobase + 0x7b); - cbl = inb(iobase + 0x7a); - p += sprintf(p, "Cable: ATA-%d" - " ATA-%d\n", - (cbl & 0x02) ? 33 : 66, - (cbl & 0x01) ? 33 : 66); - p += sprintf(p, "\n"); - } - { - u8 c2, c3; - /* older revs don't have these registers mapped - * into io space */ - pci_read_config_byte(dev, 0x43, &c0); - pci_read_config_byte(dev, 0x47, &c1); - pci_read_config_byte(dev, 0x4b, &c2); - pci_read_config_byte(dev, 0x4f, &c3); - - p += sprintf(p, "Mode: %s %s" - " %s %s\n", - (c0 & 0x10) ? "UDMA" : (c0 & 0x20) ? "DMA " : - (c0 & 0x80) ? "PIO " : "off ", - (c1 & 0x10) ? "UDMA" : (c1 & 0x20) ? "DMA " : - (c1 & 0x80) ? "PIO " : "off ", - (c2 & 0x10) ? "UDMA" : (c2 & 0x20) ? "DMA " : - (c2 & 0x80) ? "PIO " : "off ", - (c3 & 0x10) ? "UDMA" : (c3 & 0x20) ? "DMA " : - (c3 & 0x80) ? "PIO " : "off "); - } - } -#endif +/* + * Hold all the highpoint quirks and revision information in one + * place. + */ -static u32 hpt_revision (struct pci_dev *dev) +struct hpt_info +{ + u8 max_mode; /* Speeds allowed */ + int revision; /* Chipset revision */ + int flags; /* Chipset properties */ +#define PLL_MODE 1 +#define IS_372N 2 + /* Speed table */ + struct chipset_bus_clock_list_entry *speed; +}; + +/* + * This wants fixing so that we do everything not by classrev + * (which breaks on the newest chips) but by creating an + * enumeration of chip variants and using that + */ + +static __devinit u32 hpt_revision (struct pci_dev *dev) { u32 class_rev; pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); @@ -507,37 +497,33 @@ static u32 hpt_revision (struct pci_dev *dev) return class_rev; } -static u32 hpt_minimum_revision (struct pci_dev *dev, int revision) -{ - unsigned int class_rev = hpt_revision(dev); - revision--; - return ((int) (class_rev > revision) ? 1 : 0); -} - static int check_in_drive_lists(ide_drive_t *drive, const char **list); static u8 hpt3xx_ratemask (ide_drive_t *drive) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 mode = 0; - if (hpt_minimum_revision(dev, 8)) { /* HPT374 */ + /* FIXME: TODO - move this to set info->mode once at boot */ + + if (info->revision >= 8) { /* HPT374 */ mode = (HPT374_ALLOW_ATA133_6) ? 4 : 3; - } else if (hpt_minimum_revision(dev, 7)) { /* HPT371 */ + } else if (info->revision >= 7) { /* HPT371 */ mode = (HPT371_ALLOW_ATA133_6) ? 4 : 3; - } else if (hpt_minimum_revision(dev, 6)) { /* HPT302 */ + } else if (info->revision >= 6) { /* HPT302 */ mode = (HPT302_ALLOW_ATA133_6) ? 4 : 3; - } else if (hpt_minimum_revision(dev, 5)) { /* HPT372 */ + } else if (info->revision >= 5) { /* HPT372 */ mode = (HPT372_ALLOW_ATA133_6) ? 4 : 3; - } else if (hpt_minimum_revision(dev, 4)) { /* HPT370A */ + } else if (info->revision >= 4) { /* HPT370A */ mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2; - } else if (hpt_minimum_revision(dev, 3)) { /* HPT370 */ + } else if (info->revision >= 3) { /* HPT370 */ mode = (HPT370_ALLOW_ATA100_5) ? 3 : 2; mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : mode; } else { /* HPT366 and HPT368 */ mode = (check_in_drive_lists(drive, bad_ata33)) ? 0 : 2; } - if (!eighty_ninty_three(drive) && (mode)) + if (!eighty_ninty_three(drive) && mode) mode = min(mode, (u8)1); return mode; } @@ -549,7 +535,8 @@ static u8 hpt3xx_ratemask (ide_drive_t *drive) static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 mode = hpt3xx_ratemask(drive); if (drive->media != ide_disk) @@ -561,7 +548,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed) break; case 0x03: speed = min(speed, (u8)XFER_UDMA_5); - if (hpt_minimum_revision(dev, 5)) + if (info->revision >= 5) break; if (check_in_drive_lists(drive, bad_ata100_5)) speed = min(speed, (u8)XFER_UDMA_4); @@ -571,7 +558,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed) /* * CHECK ME, Does this need to be set to 5 ?? */ - if (hpt_minimum_revision(dev, 3)) + if (info->revision >= 3) break; if ((check_in_drive_lists(drive, bad_ata66_4)) || (!(HPT366_ALLOW_ATA66_4))) @@ -585,7 +572,7 @@ static u8 hpt3xx_ratefilter (ide_drive_t *drive, u8 speed) /* * CHECK ME, Does this need to be set to 5 ?? */ - if (hpt_minimum_revision(dev, 3)) + if (info->revision >= 3) break; if (check_in_drive_lists(drive, bad_ata33)) speed = min(speed, (u8)XFER_MW_DMA_2); @@ -624,11 +611,12 @@ static unsigned int pci_bus_clock_list (u8 speed, struct chipset_bus_clock_list_ static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = hwif->pci_dev; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 speed = hpt3xx_ratefilter(drive, xferspeed); -// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); u8 regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; - u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 regfast = (hwif->channel) ? 0x55 : 0x51; u8 drive_fast = 0; u32 reg1 = 0, reg2 = 0; @@ -636,16 +624,11 @@ static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed) * Disable the "fast interrupt" prediction. */ pci_read_config_byte(dev, regfast, &drive_fast); -#if 0 - if (drive_fast & 0x02) - pci_write_config_byte(dev, regfast, drive_fast & ~0x20); -#else if (drive_fast & 0x80) pci_write_config_byte(dev, regfast, drive_fast & ~0x80); -#endif - reg2 = pci_bus_clock_list(speed, - (struct chipset_bus_clock_list_entry *) pci_get_drvdata(dev)); + reg2 = pci_bus_clock_list(speed, info->speed); + /* * Disable on-chip PIO FIFO/buffer * (to avoid problems handling I/O errors later) @@ -665,10 +648,11 @@ static int hpt36x_tune_chipset(ide_drive_t *drive, u8 xferspeed) static int hpt370_tune_chipset(ide_drive_t *drive, u8 xferspeed) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = hwif->pci_dev; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 speed = hpt3xx_ratefilter(drive, xferspeed); -// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); - u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 regfast = (drive->hwif->channel) ? 0x55 : 0x51; u8 drive_pci = 0x40 + (drive->dn * 4); u8 new_fast = 0, drive_fast = 0; u32 list_conf = 0, drive_conf = 0; @@ -693,17 +677,13 @@ static int hpt370_tune_chipset(ide_drive_t *drive, u8 xferspeed) if (new_fast != drive_fast) pci_write_config_byte(dev, regfast, new_fast); - list_conf = pci_bus_clock_list(speed, - (struct chipset_bus_clock_list_entry *) - pci_get_drvdata(dev)); + list_conf = pci_bus_clock_list(speed, info->speed); pci_read_config_dword(dev, drive_pci, &drive_conf); list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); - if (speed < XFER_MW_DMA_0) { + if (speed < XFER_MW_DMA_0) list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ - } - pci_write_config_dword(dev, drive_pci, list_conf); return ide_config_drive_speed(drive, speed); @@ -711,10 +691,11 @@ static int hpt370_tune_chipset(ide_drive_t *drive, u8 xferspeed) static int hpt372_tune_chipset(ide_drive_t *drive, u8 xferspeed) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = hwif->pci_dev; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 speed = hpt3xx_ratefilter(drive, xferspeed); -// u8 speed = ide_rate_filter(hpt3xx_ratemask(drive), xferspeed); - u8 regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + u8 regfast = (drive->hwif->channel) ? 0x55 : 0x51; u8 drive_fast = 0, drive_pci = 0x40 + (drive->dn * 4); u32 list_conf = 0, drive_conf = 0; u32 conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; @@ -726,10 +707,8 @@ static int hpt372_tune_chipset(ide_drive_t *drive, u8 xferspeed) pci_read_config_byte(dev, regfast, &drive_fast); drive_fast &= ~0x07; pci_write_config_byte(dev, regfast, drive_fast); - - list_conf = pci_bus_clock_list(speed, - (struct chipset_bus_clock_list_entry *) - pci_get_drvdata(dev)); + + list_conf = pci_bus_clock_list(speed, info->speed); pci_read_config_dword(dev, drive_pci, &drive_conf); list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); if (speed < XFER_MW_DMA_0) @@ -741,19 +720,14 @@ static int hpt372_tune_chipset(ide_drive_t *drive, u8 xferspeed) static int hpt3xx_tune_chipset (ide_drive_t *drive, u8 speed) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = ide_get_hwifdata(hwif); - if (hpt_minimum_revision(dev, 8)) + if (info->revision >= 8) return hpt372_tune_chipset(drive, speed); /* not a typo */ -#if 0 - else if (hpt_minimum_revision(dev, 7)) - hpt371_tune_chipset(drive, speed); - else if (hpt_minimum_revision(dev, 6)) - hpt302_tune_chipset(drive, speed); -#endif - else if (hpt_minimum_revision(dev, 5)) + else if (info->revision >= 5) return hpt372_tune_chipset(drive, speed); - else if (hpt_minimum_revision(dev, 3)) + else if (info->revision >= 3) return hpt370_tune_chipset(drive, speed); else /* hpt368: hpt_minimum_revision(dev, 2) */ return hpt36x_tune_chipset(drive, speed); @@ -779,8 +753,14 @@ static void hpt3xx_tune_drive (ide_drive_t *drive, u8 pio) static int config_chipset_for_dma (ide_drive_t *drive) { u8 speed = ide_dma_speed(drive, hpt3xx_ratemask(drive)); + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = ide_get_hwifdata(hwif); - if (!(speed)) + if (!speed) + return 0; + + /* If we don't have any timings we can't do a lot */ + if (info->speed == NULL) return 0; (void) hpt3xx_tune_chipset(drive, speed); @@ -794,7 +774,7 @@ static int hpt3xx_quirkproc (ide_drive_t *drive) static void hpt3xx_intrproc (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; if (drive->quirk_list) return; @@ -804,24 +784,26 @@ static void hpt3xx_intrproc (ide_drive_t *drive) static void hpt3xx_maskproc (ide_drive_t *drive, int mask) { - struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = ide_get_hwifdata(hwif); + struct pci_dev *dev = hwif->pci_dev; if (drive->quirk_list) { - if (hpt_minimum_revision(dev,3)) { + if (info->revision >= 3) { u8 reg5a = 0; pci_read_config_byte(dev, 0x5a, ®5a); if (((reg5a & 0x10) >> 4) != mask) pci_write_config_byte(dev, 0x5a, mask ? (reg5a | 0x10) : (reg5a & ~0x10)); } else { if (mask) { - disable_irq(HWIF(drive)->irq); + disable_irq(hwif->irq); } else { - enable_irq(HWIF(drive)->irq); + enable_irq(hwif->irq); } } } else { if (IDE_CONTROL_REG) - HWIF(drive)->OUTB(mask ? (drive->ctl | 2) : + hwif->OUTB(mask ? (drive->ctl | 2) : (drive->ctl & ~2), IDE_CONTROL_REG); } @@ -829,12 +811,12 @@ static void hpt3xx_maskproc (ide_drive_t *drive, int mask) static int hpt366_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct hd_driveid *id = drive->id; drive->init_speed = 0; - if (id && (id->capability & 1) && drive->autodma) { + if ((id->capability & 1) && drive->autodma) { if (ide_use_dma(drive)) { if (config_chipset_for_dma(drive)) @@ -868,15 +850,6 @@ static int hpt366_ide_dma_lostirq (ide_drive_t *drive) drive->name, __FUNCTION__, reg50h, reg52h, reg5ah); if (reg5ah & 0x10) pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); -#if 0 - /* how about we flush and reset, mmmkay? */ - pci_write_config_byte(dev, 0x51, 0x1F); - /* fall through to a reset */ - case dma_start: - case ide_dma_end: - /* reset the chips state over and over.. */ - pci_write_config_byte(dev, 0x51, 0x13); -#endif return __ide_dma_lostirq(drive); } @@ -919,7 +892,7 @@ static void hpt370_lostirq_timeout (ide_drive_t *drive) u8 dma_stat = 0, dma_cmd = 0; pci_read_config_byte(HWIF(drive)->pci_dev, reginfo, &bfifo); - printk("%s: %d bytes in FIFO\n", drive->name, bfifo); + printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo); hpt370_clear_engine(drive); /* get dma command mode */ dma_cmd = hwif->INB(hwif->dma_command); @@ -1047,15 +1020,6 @@ static void hpt372n_rw_disk(ide_drive_t *drive, struct request *rq) static void hpt3xx_reset (ide_drive_t *drive) { -#if 0 - unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); - u8 reset = (HWIF(drive)->channel) ? 0x80 : 0x40; - u8 reg59h = 0; - - pci_read_config_byte(HWIF(drive)->pci_dev, 0x59, ®59h); - pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h|reset); - pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h); -#endif } static int hpt3xx_tristate (ide_drive_t * drive, int state) @@ -1065,8 +1029,6 @@ static int hpt3xx_tristate (ide_drive_t * drive, int state) u8 reg59h = 0, reset = (hwif->channel) ? 0x80 : 0x40; u8 regXXh = 0, state_reg= (hwif->channel) ? 0x57 : 0x53; -// hwif->bus_state = state; - pci_read_config_byte(dev, 0x59, ®59h); pci_read_config_byte(dev, state_reg, ®XXh); @@ -1093,7 +1055,7 @@ static int hpt3xx_tristate (ide_drive_t * drive, int state) #define TRISTATE_BIT 0x8000 static int hpt370_busproc(ide_drive_t * drive, int state) { - ide_hwif_t *hwif = HWIF(drive); + ide_hwif_t *hwif = drive->hwif; struct pci_dev *dev = hwif->pci_dev; u8 tristate = 0, resetmask = 0, bus_reg = 0; u16 tri_reg; @@ -1148,33 +1110,44 @@ static int hpt370_busproc(ide_drive_t * drive, int state) return 0; } -static int __devinit init_hpt37x(struct pci_dev *dev) +static void __devinit hpt366_clocking(ide_hwif_t *hwif) { + u32 reg1 = 0; + struct hpt_info *info = ide_get_hwifdata(hwif); + + pci_read_config_dword(hwif->pci_dev, 0x40, ®1); + + /* detect bus speed by looking at control reg timing: */ + switch((reg1 >> 8) & 7) { + case 5: + info->speed = forty_base_hpt366; + break; + case 9: + info->speed = twenty_five_base_hpt366; + break; + case 7: + default: + info->speed = thirty_three_base_hpt366; + break; + } +} + +static void __devinit hpt37x_clocking(ide_hwif_t *hwif) +{ + struct hpt_info *info = ide_get_hwifdata(hwif); + struct pci_dev *dev = hwif->pci_dev; int adjust, i; u16 freq; u32 pll; u8 reg5bh; - u8 reg5ah = 0; - unsigned long dmabase = pci_resource_start(dev, 4); - u8 did, rid; - int is_372n = 0; - pci_read_config_byte(dev, 0x5a, ®5ah); - /* interrupt force enable */ - pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); - - if(dmabase) - { - did = inb(dmabase + 0x22); - rid = inb(dmabase + 0x28); - - if((did == 4 && rid == 6) || (did == 5 && rid > 1)) - is_372n = 1; - } - /* * default to pci clock. make sure MA15/16 are set to output - * to prevent drives having problems with 40-pin cables. + * to prevent drives having problems with 40-pin cables. Needed + * for some drives such as IBM-DTLA which will not enter ready + * state on reset when PDIAG is a input. + * + * ToDo: should we set 0x21 when using PLL mode ? */ pci_write_config_byte(dev, 0x5b, 0x23); @@ -1197,9 +1170,7 @@ static int __devinit init_hpt37x(struct pci_dev *dev) * Currently we always set up the PLL for the 372N */ - pci_set_drvdata(dev, NULL); - - if(is_372n) + if(info->flags & IS_372N) { printk(KERN_INFO "hpt: HPT372N detected, using 372N timing.\n"); if(freq < 0x55) @@ -1227,39 +1198,38 @@ static int __devinit init_hpt37x(struct pci_dev *dev) pll = F_LOW_PCI_66; if (pll == F_LOW_PCI_33) { - if (hpt_minimum_revision(dev,8)) - pci_set_drvdata(dev, (void *) thirty_three_base_hpt374); - else if (hpt_minimum_revision(dev,5)) - pci_set_drvdata(dev, (void *) thirty_three_base_hpt372); - else if (hpt_minimum_revision(dev,4)) - pci_set_drvdata(dev, (void *) thirty_three_base_hpt370a); + if (info->revision >= 8) + info->speed = thirty_three_base_hpt374; + else if (info->revision >= 5) + info->speed = thirty_three_base_hpt372; + else if (info->revision >= 4) + info->speed = thirty_three_base_hpt370a; else - pci_set_drvdata(dev, (void *) thirty_three_base_hpt370); - printk("HPT37X: using 33MHz PCI clock\n"); + info->speed = thirty_three_base_hpt370; + printk(KERN_DEBUG "HPT37X: using 33MHz PCI clock\n"); } else if (pll == F_LOW_PCI_40) { /* Unsupported */ } else if (pll == F_LOW_PCI_50) { - if (hpt_minimum_revision(dev,8)) - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); - else if (hpt_minimum_revision(dev,5)) - pci_set_drvdata(dev, (void *) fifty_base_hpt372); - else if (hpt_minimum_revision(dev,4)) - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + if (info->revision >= 8) + info->speed = fifty_base_hpt370a; + else if (info->revision >= 5) + info->speed = fifty_base_hpt372; + else if (info->revision >= 4) + info->speed = fifty_base_hpt370a; else - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); - printk("HPT37X: using 50MHz PCI clock\n"); + info->speed = fifty_base_hpt370a; + printk(KERN_DEBUG "HPT37X: using 50MHz PCI clock\n"); } else { - if (hpt_minimum_revision(dev,8)) - { + if (info->revision >= 8) { printk(KERN_ERR "HPT37x: 66MHz timings are not supported.\n"); } - else if (hpt_minimum_revision(dev,5)) - pci_set_drvdata(dev, (void *) sixty_six_base_hpt372); - else if (hpt_minimum_revision(dev,4)) - pci_set_drvdata(dev, (void *) sixty_six_base_hpt370a); + else if (info->revision >= 5) + info->speed = sixty_six_base_hpt372; + else if (info->revision >= 4) + info->speed = sixty_six_base_hpt370a; else - pci_set_drvdata(dev, (void *) sixty_six_base_hpt370); - printk("HPT37X: using 66MHz PCI clock\n"); + info->speed = sixty_six_base_hpt370; + printk(KERN_DEBUG "HPT37X: using 66MHz PCI clock\n"); } } @@ -1269,11 +1239,19 @@ static int __devinit init_hpt37x(struct pci_dev *dev) * result in slow reads when using a 33MHz PCI clock. we also * don't like to use the PLL because it will cause glitches * on PRST/SRST when the HPT state engine gets reset. + * + * ToDo: Use 66MHz PLL when ATA133 devices are present on a + * 372 device so we can get ATA133 support */ - if (pci_get_drvdata(dev)) + if (info->speed) goto init_hpt37X_done; + + info->flags |= PLL_MODE; /* + * FIXME: make this work correctly, esp with 372N as per + * reference driver code. + * * adjust PLL based upon PCI clock, enable it, and wait for * stabilization. */ @@ -1298,14 +1276,14 @@ static int __devinit init_hpt37x(struct pci_dev *dev) pci_write_config_dword(dev, 0x5c, pll & ~0x100); pci_write_config_byte(dev, 0x5b, 0x21); - if (hpt_minimum_revision(dev,8)) - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); - else if (hpt_minimum_revision(dev,5)) - pci_set_drvdata(dev, (void *) fifty_base_hpt372); - else if (hpt_minimum_revision(dev,4)) - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + if (info->revision >= 8) + info->speed = fifty_base_hpt370a; + else if (info->revision >= 5) + info->speed = fifty_base_hpt372; + else if (info->revision >= 4) + info->speed = fifty_base_hpt370a; else - pci_set_drvdata(dev, (void *) fifty_base_hpt370a); + info->speed = fifty_base_hpt370a; printk("HPT37X: using 50MHz internal PLL\n"); goto init_hpt37X_done; } @@ -1318,10 +1296,22 @@ pll_recal: } init_hpt37X_done: + if (!info->speed) + printk(KERN_ERR "HPT37X%s: unknown bus timing [%d %d].\n", + (info->flags & IS_372N)?"N":"", pll, freq); /* reset state engine */ pci_write_config_byte(dev, 0x50, 0x37); pci_write_config_byte(dev, 0x54, 0x37); udelay(100); +} + +static int __devinit init_hpt37x(struct pci_dev *dev) +{ + u8 reg5ah; + + pci_read_config_byte(dev, 0x5a, ®5ah); + /* interrupt force enable */ + pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); return 0; } @@ -1338,59 +1328,27 @@ static int __devinit init_hpt366(struct pci_dev *dev) pci_write_config_byte(dev, 0x51, drive_fast & ~0x80); pci_read_config_dword(dev, 0x40, ®1); - /* detect bus speed by looking at control reg timing: */ - switch((reg1 >> 8) & 7) { - case 5: - pci_set_drvdata(dev, (void *) forty_base_hpt366); - break; - case 9: - pci_set_drvdata(dev, (void *) twenty_five_base_hpt366); - break; - case 7: - default: - pci_set_drvdata(dev, (void *) thirty_three_base_hpt366); - break; - } - - if (!pci_get_drvdata(dev)) - { - printk(KERN_ERR "hpt366: unknown bus timing.\n"); - pci_set_drvdata(dev, NULL); - } return 0; } static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const char *name) { int ret = 0; - u8 test = 0; - + /* FIXME: Not portable */ if (dev->resource[PCI_ROM_RESOURCE].start) pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); - pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test); - if (test != (L1_CACHE_BYTES / 4)) - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, - (L1_CACHE_BYTES / 4)); - - pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test); - if (test != 0x78) - pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4)); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78); + pci_write_config_byte(dev, PCI_MIN_GNT, 0x08); + pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); - pci_read_config_byte(dev, PCI_MIN_GNT, &test); - if (test != 0x08) - pci_write_config_byte(dev, PCI_MIN_GNT, 0x08); - - pci_read_config_byte(dev, PCI_MAX_LAT, &test); - if (test != 0x08) - pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); - - if (hpt_minimum_revision(dev, 3)) { + if (hpt_revision(dev) >= 3) ret = init_hpt37x(dev); - } else { - ret =init_hpt366(dev); - } + else + ret = init_hpt366(dev); + if (ret) return ret; @@ -1400,27 +1358,16 @@ static unsigned int __devinit init_chipset_hpt366(struct pci_dev *dev, const cha static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) { struct pci_dev *dev = hwif->pci_dev; + struct hpt_info *info = ide_get_hwifdata(hwif); u8 ata66 = 0, regmask = (hwif->channel) ? 0x01 : 0x02; - u8 did, rid; - unsigned long dmabase = hwif->dma_base; - int is_372n = 0; - if(dmabase) - { - did = inb(dmabase + 0x22); - rid = inb(dmabase + 0x28); - - if((did == 4 && rid == 6) || (did == 5 && rid > 1)) - is_372n = 1; - } - hwif->tuneproc = &hpt3xx_tune_drive; hwif->speedproc = &hpt3xx_tune_chipset; hwif->quirkproc = &hpt3xx_quirkproc; hwif->intrproc = &hpt3xx_intrproc; hwif->maskproc = &hpt3xx_maskproc; - if(is_372n) + if(info->flags & IS_372N) hwif->rw_disk = &hpt372n_rw_disk; /* @@ -1428,7 +1375,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) * address lines to access an external eeprom. To read valid * cable detect state the pins must be enabled as inputs. */ - if (hpt_minimum_revision(dev, 8) && PCI_FUNC(dev->devfn) & 1) { + if (info->revision >= 8 && (PCI_FUNC(dev->devfn) & 1)) { /* * HPT374 PCI function 1 * - set bit 15 of reg 0x52 to enable TCBLID as input @@ -1443,7 +1390,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) pci_read_config_byte(dev, 0x5a, &ata66); pci_write_config_word(dev, 0x52, mcr3); pci_write_config_word(dev, 0x56, mcr6); - } else if (hpt_minimum_revision(dev, 3)) { + } else if (info->revision >= 3) { /* * HPT370/372 and 374 pcifn 0 * - clear bit 0 of 0x5b to enable P/SCBLID as inputs @@ -1470,7 +1417,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) hwif->serialized = hwif->mate->serialized = 1; #endif - if (hpt_minimum_revision(dev,3)) { + if (info->revision >= 3) { u8 reg5ah = 0; pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); /* @@ -1480,8 +1427,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) */ hwif->resetproc = &hpt3xx_reset; hwif->busproc = &hpt370_busproc; -// hwif->drives[0].autotune = hwif->drives[1].autotune = 1; - } else if (hpt_minimum_revision(dev,2)) { + } else if (info->revision >= 2) { hwif->resetproc = &hpt3xx_reset; hwif->busproc = &hpt3xx_tristate; } else { @@ -1502,18 +1448,18 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) hwif->udma_four = ((ata66 & regmask) ? 0 : 1); hwif->ide_dma_check = &hpt366_config_drive_xfer_rate; - if (hpt_minimum_revision(dev,8)) { + if (info->revision >= 8) { hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq; hwif->ide_dma_end = &hpt374_ide_dma_end; - } else if (hpt_minimum_revision(dev,5)) { + } else if (info->revision >= 5) { hwif->ide_dma_test_irq = &hpt374_ide_dma_test_irq; hwif->ide_dma_end = &hpt374_ide_dma_end; - } else if (hpt_minimum_revision(dev,3)) { + } else if (info->revision >= 3) { hwif->dma_start = &hpt370_ide_dma_start; hwif->ide_dma_end = &hpt370_ide_dma_end; hwif->ide_dma_timeout = &hpt370_ide_dma_timeout; hwif->ide_dma_lostirq = &hpt370_ide_dma_lostirq; - } else if (hpt_minimum_revision(dev,2)) + } else if (info->revision >= 2) hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq; else hwif->ide_dma_lostirq = &hpt366_ide_dma_lostirq; @@ -1526,6 +1472,7 @@ static void __devinit init_hwif_hpt366(ide_hwif_t *hwif) static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase) { + struct hpt_info *info = ide_get_hwifdata(hwif); u8 masterdma = 0, slavedma = 0; u8 dma_new = 0, dma_old = 0; u8 primary = hwif->channel ? 0x4b : 0x43; @@ -1535,8 +1482,7 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase) if (!dmabase) return; - if(pci_get_drvdata(hwif->pci_dev) == NULL) - { + if(info->speed == NULL) { printk(KERN_WARNING "hpt: no known IDE timings, disabling DMA.\n"); return; } @@ -1559,6 +1505,40 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase) ide_setup_dma(hwif, dmabase, 8); } +/* + * We "borrow" this hook in order to set the data structures + * up early enough before dma or init_hwif calls are made. + */ + +static void __devinit init_iops_hpt366(ide_hwif_t *hwif) +{ + struct hpt_info *info = kmalloc(sizeof(struct hpt_info), GFP_KERNEL); + unsigned long dmabase = pci_resource_start(hwif->pci_dev, 4); + u8 did, rid; + + if(info == NULL) { + printk(KERN_WARNING "hpt366: out of memory.\n"); + return; + } + memset(info, 0, sizeof(struct hpt_info)); + ide_set_hwifdata(hwif, info); + + if(dmabase) { + did = inb(dmabase + 0x22); + rid = inb(dmabase + 0x28); + + if((did == 4 && rid == 6) || (did == 5 && rid > 1)) + info->flags |= IS_372N; + } + + info->revision = hpt_revision(hwif->pci_dev); + + if (info->revision >= 3) + hpt37x_clocking(hwif); + else + hpt366_clocking(hwif); +} + static int __devinit init_setup_hpt374(struct pci_dev *dev, ide_pci_device_t *d) { struct pci_dev *findev = NULL; @@ -1646,6 +1626,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT366", .init_setup = init_setup_hpt366, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, @@ -1656,6 +1637,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT372A", .init_setup = init_setup_hpt37x, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, @@ -1665,6 +1647,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT302", .init_setup = init_setup_hpt37x, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, @@ -1674,6 +1657,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT371", .init_setup = init_setup_hpt37x, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, @@ -1683,6 +1667,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT374", .init_setup = init_setup_hpt374, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, /* 4 */ @@ -1692,6 +1677,7 @@ static ide_pci_device_t hpt366_chipsets[] __devinitdata = { .name = "HPT372N", .init_setup = init_setup_hpt37x, .init_chipset = init_chipset_hpt366, + .init_iops = init_iops_hpt366, .init_hwif = init_hwif_hpt366, .init_dma = init_dma_hpt366, .channels = 2, /* 4 */ diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c new file mode 100644 index 00000000000..e440036e651 --- /dev/null +++ b/drivers/ide/pci/it821x.c @@ -0,0 +1,812 @@ + +/* + * linux/drivers/ide/pci/it821x.c Version 0.09 December 2004 + * + * Copyright (C) 2004 Red Hat <alan@redhat.com> + * + * May be copied or modified under the terms of the GNU General Public License + * Based in part on the ITE vendor provided SCSI driver. + * + * Documentation available from + * http://www.ite.com.tw/pc/IT8212F_V04.pdf + * Some other documents are NDA. + * + * The ITE8212 isn't exactly a standard IDE controller. It has two + * modes. In pass through mode then it is an IDE controller. In its smart + * mode its actually quite a capable hardware raid controller disguised + * as an IDE controller. Smart mode only understands DMA read/write and + * identify, none of the fancier commands apply. The IT8211 is identical + * in other respects but lacks the raid mode. + * + * Errata: + * o Rev 0x10 also requires master/slave hold the same DMA timings and + * cannot do ATAPI MWDMA. + * o The identify data for raid volumes lacks CHS info (technically ok) + * but also fails to set the LBA28 and other bits. We fix these in + * the IDE probe quirk code. + * o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode + * raid then the controller firmware dies + * o Smart mode without RAID doesn't clear all the necessary identify + * bits to reduce the command set to the one used + * + * This has a few impacts on the driver + * - In pass through mode we do all the work you would expect + * - In smart mode the clocking set up is done by the controller generally + * but we must watch the other limits and filter. + * - There are a few extra vendor commands that actually talk to the + * controller but only work PIO with no IRQ. + * + * Vendor areas of the identify block in smart mode are used for the + * timing and policy set up. Each HDD in raid mode also has a serial + * block on the disk. The hardware extra commands are get/set chip status, + * rebuild, get rebuild status. + * + * In Linux the driver supports pass through mode as if the device was + * just another IDE controller. If the smart mode is running then + * volumes are managed by the controller firmware and each IDE "disk" + * is a raid volume. Even more cute - the controller can do automated + * hotplug and rebuild. + * + * The pass through controller itself is a little demented. It has a + * flaw that it has a single set of PIO/MWDMA timings per channel so + * non UDMA devices restrict each others performance. It also has a + * single clock source per channel so mixed UDMA100/133 performance + * isn't perfect and we have to pick a clock. Thankfully none of this + * matters in smart mode. ATAPI DMA is not currently supported. + * + * It seems the smart mode is a win for RAID1/RAID10 but otherwise not. + * + * TODO + * - ATAPI UDMA is ok but not MWDMA it seems + * - RAID configuration ioctls + * - Move to libata once it grows up + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +struct it821x_dev +{ + unsigned int smart:1, /* Are we in smart raid mode */ + timing10:1; /* Rev 0x10 */ + u8 clock_mode; /* 0, ATA_50 or ATA_66 */ + u8 want[2][2]; /* Mode/Pri log for master slave */ + /* We need these for switching the clock when DMA goes on/off + The high byte is the 66Mhz timing */ + u16 pio[2]; /* Cached PIO values */ + u16 mwdma[2]; /* Cached MWDMA values */ + u16 udma[2]; /* Cached UDMA values (per drive) */ +}; + +#define ATA_66 0 +#define ATA_50 1 +#define ATA_ANY 2 + +#define UDMA_OFF 0 +#define MWDMA_OFF 0 + +/* + * We allow users to force the card into non raid mode without + * flashing the alternative BIOS. This is also neccessary right now + * for embedded platforms that cannot run a PC BIOS but are using this + * device. + */ + +static int it8212_noraid; + +/** + * it821x_program - program the PIO/MWDMA registers + * @drive: drive to tune + * + * Program the PIO/MWDMA timing for this channel according to the + * current clock. + */ + +static void it821x_program(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + u8 conf; + + /* Program PIO/MWDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + pci_write_config_byte(hwif->pci_dev, 0x54 + 4 * channel, conf); +} + +/** + * it821x_program_udma - program the UDMA registers + * @drive: drive to tune + * + * Program the UDMA timing for this drive according to the + * current clock. + */ + +static void it821x_program_udma(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + int unit = drive->select.b.unit; + u8 conf; + + /* Program UDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + if(itdev->timing10 == 0) + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + unit, conf); + else { + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel, conf); + pci_write_config_byte(hwif->pci_dev, 0x56 + 4 * channel + 1, conf); + } +} + + +/** + * it821x_clock_strategy + * @hwif: hardware interface + * + * Select between the 50 and 66Mhz base clocks to get the best + * results for this interface. + */ + +static void it821x_clock_strategy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + + u8 unit = drive->select.b.unit; + ide_drive_t *pair = &hwif->drives[1-unit]; + + int clock, altclock; + u8 v; + int sel = 0; + + if(itdev->want[0][0] > itdev->want[1][0]) { + clock = itdev->want[0][1]; + altclock = itdev->want[1][1]; + } else { + clock = itdev->want[1][1]; + altclock = itdev->want[0][1]; + } + + /* Master doesn't care does the slave ? */ + if(clock == ATA_ANY) + clock = altclock; + + /* Nobody cares - keep the same clock */ + if(clock == ATA_ANY) + return; + /* No change */ + if(clock == itdev->clock_mode) + return; + + /* Load this into the controller ? */ + if(clock == ATA_66) + itdev->clock_mode = ATA_66; + else { + itdev->clock_mode = ATA_50; + sel = 1; + } + pci_read_config_byte(hwif->pci_dev, 0x50, &v); + v &= ~(1 << (1 + hwif->channel)); + v |= sel << (1 + hwif->channel); + pci_write_config_byte(hwif->pci_dev, 0x50, v); + + /* + * Reprogram the UDMA/PIO of the pair drive for the switch + * MWDMA will be dealt with by the dma switcher + */ + if(pair && itdev->udma[1-unit] != UDMA_OFF) { + it821x_program_udma(pair, itdev->udma[1-unit]); + it821x_program(pair, itdev->pio[1-unit]); + } + /* + * Reprogram the UDMA/PIO of our drive for the switch. + * MWDMA will be dealt with by the dma switcher + */ + if(itdev->udma[unit] != UDMA_OFF) { + it821x_program_udma(drive, itdev->udma[unit]); + it821x_program(drive, itdev->pio[unit]); + } +} + +/** + * it821x_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. This + * is all modes to ATA133 clipped by drive cable setup. + */ + +static u8 it821x_ratemask (ide_drive_t *drive) +{ + u8 mode = 4; + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * it821x_tuneproc - tune a drive + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. By the time we are called the mode has been + * modified as neccessary to handle the absence of seperate + * master/slave timers for MWDMA/PIO. + * + * This code is only used in pass through mode. + */ + +static void it821x_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + + /* Spec says 89 ref driver uses 88 */ + static u16 pio[] = { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 }; + static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY }; + + if(itdev->smart) + return; + + /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */ + itdev->want[unit][1] = pio_want[mode_wanted]; + itdev->want[unit][0] = 1; /* PIO is lowest priority */ + itdev->pio[unit] = pio[mode_wanted]; + it821x_clock_strategy(drive); + it821x_program(drive, itdev->pio[unit]); +} + +/** + * it821x_tune_mwdma - tune a channel for MWDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing MWDMA in pass through mode. The caller + * must manage the whole lack of per device MWDMA/PIO timings and + * the shared MWDMA/PIO timing register. + */ + +static void it821x_tune_mwdma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 dma[] = { 0x8866, 0x3222, 0x3121 }; + static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY }; + + itdev->want[unit][1] = mwdma_want[mode_wanted]; + itdev->want[unit][0] = 2; /* MWDMA is low priority */ + itdev->mwdma[unit] = dma[mode_wanted]; + itdev->udma[unit] = UDMA_OFF; + + /* UDMA bits off - Revision 0x10 do them in pairs */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf |= channel ? 0x60: 0x18; + else + conf |= 1 << (3 + 2 * channel + unit); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it821x_clock_strategy(drive); + /* FIXME: do we need to program this ? */ + /* it821x_program(drive, itdev->mwdma[unit]); */ +} + +/** + * it821x_tune_udma - tune a channel for UDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing UDMA modes in pass through. + */ + +static void it821x_tune_udma (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + int channel = hwif->channel; + u8 conf; + + static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 }; + static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 }; + + itdev->want[unit][1] = udma_want[mode_wanted]; + itdev->want[unit][0] = 3; /* UDMA is high priority */ + itdev->mwdma[unit] = MWDMA_OFF; + itdev->udma[unit] = udma[mode_wanted]; + if(mode_wanted >= 5) + itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */ + + /* UDMA on. Again revision 0x10 must do the pair */ + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(itdev->timing10) + conf &= channel ? 0x9F: 0xE7; + else + conf &= ~ (1 << (3 + 2 * channel + unit)); + pci_write_config_byte(hwif->pci_dev, 0x50, conf); + + it821x_clock_strategy(drive); + it821x_program_udma(drive, itdev->udma[unit]); + +} + +/** + * config_it821x_chipset_for_pio - set drive timings + * @drive: drive to tune + * @speed we want + * + * Compute the best pio mode we can for a given device. We must + * pick a speed that does not cause problems with the other device + * on the cable. + */ + +static void config_it821x_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + u8 unit = drive->select.b.unit; + ide_hwif_t *hwif = drive->hwif; + ide_drive_t *pair = &hwif->drives[1-unit]; + u8 speed = 0, set_pio = ide_get_best_pio_mode(drive, 255, 5, NULL); + u8 pair_pio; + + /* We have to deal with this mess in pairs */ + if(pair != NULL) { + pair_pio = ide_get_best_pio_mode(pair, 255, 5, NULL); + /* Trim PIO to the slowest of the master/slave */ + if(pair_pio < set_pio) + set_pio = pair_pio; + } + it821x_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + /* XXX - We trim to the lowest of the pair so the other drive + will always be fine at this point until we do hotplug passthru */ + + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +/** + * it821x_dma_read - DMA hook + * @drive: drive for DMA + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. In addition the rev 0x10 device only works if the same + * timing value is loaded into the master and slave UDMA clock + * so we must also reload that. + * + * FIXME: we could figure out in advance if we need to do reloads + */ + +static void it821x_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int unit = drive->select.b.unit; + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->mwdma[unit]); + else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10) + it821x_program_udma(drive, itdev->udma[unit]); + ide_dma_start(drive); +} + +/** + * it821x_dma_write - DMA hook + * @drive: drive for DMA stop + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + */ + +static int it821x_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + int unit = drive->select.b.unit; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int ret = __ide_dma_end(drive); + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->pio[unit]); + return ret; +} + + +/** + * it821x_tune_chipset - set controller timings + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Tune the ITE chipset for the desired mode. If we can't achieve + * the desired mode then tune for a lower one, but ultimately + * make the thing work. + */ + +static int it821x_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + u8 speed = ide_rate_filter(it821x_ratemask(drive), xferspeed); + + if(!itdev->smart) { + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + it821x_tuneproc(drive, (speed - XFER_PIO_0)); + break; + /* MWDMA tuning is really hard because our MWDMA and PIO + timings are kept in the same place. We can switch in the + host dma on/off callbacks */ + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + it821x_tune_mwdma(drive, (speed - XFER_MW_DMA_0)); + break; + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + it821x_tune_udma(drive, (speed - XFER_UDMA_0)); + break; + default: + return 1; + } + } + /* + * In smart mode the clocking is done by the host controller + * snooping the mode we picked. The rest of it is not our problem + */ + return ide_config_drive_speed(drive, speed); +} + +/** + * config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by the IDE layer when it wants the timings set up. + */ + +static int config_chipset_for_dma (ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, it821x_ratemask(drive)); + + config_it821x_chipset_for_pio(drive, !speed); + it821x_tune_chipset(drive, speed); + return ide_dma_enable(drive); +} + +/** + * it821x_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. If the drive isn't suitable for DMA or we hit + * other problems then we will drop down to PIO and set up + * PIO appropriately + */ + +static int it821x_config_drive_for_dma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + + if (ide_use_dma(drive)) { + if (config_chipset_for_dma(drive)) + return hwif->ide_dma_on(drive); + } + config_it821x_chipset_for_pio(drive, 1); + return hwif->ide_dma_off_quietly(drive); +} + +/** + * ata66_it821x - check for 80 pin cable + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. Problematic as it seems some cards don't have + * the needed logic onboard. + */ + +static unsigned int __devinit ata66_it821x(ide_hwif_t *hwif) +{ + /* The reference driver also only does disk side */ + return 1; +} + +/** + * it821x_fixup - post init callback + * @hwif: interface + * + * This callback is run after the drives have been probed but + * before anything gets attached. It allows drivers to do any + * final tuning that is needed, or fixups to work around bugs. + */ + +static void __devinit it821x_fixups(ide_hwif_t *hwif) +{ + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int i; + + if(!itdev->smart) { + /* + * If we are in pass through mode then not much + * needs to be done, but we do bother to clear the + * IRQ mask as we may well be in PIO (eg rev 0x10) + * for now and we know unmasking is safe on this chipset. + */ + for (i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + if(drive->present) + drive->unmask = 1; + } + return; + } + /* + * Perform fixups on smart mode. We need to "lose" some + * capabilities the firmware lacks but does not filter, and + * also patch up some capability bits that it forgets to set + * in RAID mode. + */ + + for(i = 0; i < 2; i++) { + ide_drive_t *drive = &hwif->drives[i]; + struct hd_driveid *id; + u16 *idbits; + + if(!drive->present) + continue; + id = drive->id; + idbits = (u16 *)drive->id; + + /* Check for RAID v native */ + if(strstr(id->model, "Integrated Technology Express")) { + /* In raid mode the ident block is slightly buggy + We need to set the bits so that the IDE layer knows + LBA28. LBA48 and DMA ar valid */ + id->capability |= 3; /* LBA28, DMA */ + id->command_set_2 |= 0x0400; /* LBA48 valid */ + id->cfs_enable_2 |= 0x0400; /* LBA48 on */ + /* Reporting logic */ + printk(KERN_INFO "%s: IT8212 %sRAID %d volume", + drive->name, + idbits[147] ? "Bootable ":"", + idbits[129]); + if(idbits[129] != 1) + printk("(%dK stripe)", idbits[146]); + printk(".\n"); + /* Now the core code will have wrongly decided no DMA + so we need to fix this */ + hwif->ide_dma_off_quietly(drive); +#ifdef CONFIG_IDEDMA_ONLYDISK + if (drive->media == ide_disk) +#endif + hwif->ide_dma_check(drive); + } else { + /* Non RAID volume. Fixups to stop the core code + doing unsupported things */ + id->field_valid &= 1; + id->queue_depth = 0; + id->command_set_1 = 0; + id->command_set_2 &= 0xC400; + id->cfsse &= 0xC000; + id->cfs_enable_1 = 0; + id->cfs_enable_2 &= 0xC400; + id->csf_default &= 0xC000; + id->word127 = 0; + id->dlf = 0; + id->csfo = 0; + id->cfa_power = 0; + printk(KERN_INFO "%s: Performing identify fixups.\n", + drive->name); + } + } + +} + +/** + * init_hwif_it821x - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The IT8212 + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void __devinit init_hwif_it821x(ide_hwif_t *hwif) +{ + struct it821x_dev *idev = kmalloc(sizeof(struct it821x_dev), GFP_KERNEL); + u8 conf; + + if(idev == NULL) { + printk(KERN_ERR "it821x: out of memory, falling back to legacy behaviour.\n"); + goto fallback; + } + memset(idev, 0, sizeof(struct it821x_dev)); + ide_set_hwifdata(hwif, idev); + + pci_read_config_byte(hwif->pci_dev, 0x50, &conf); + if(conf & 1) { + idev->smart = 1; + hwif->atapi_dma = 0; + /* Long I/O's although allowed in LBA48 space cause the + onboard firmware to enter the twighlight zone */ + hwif->rqsize = 256; + } + + /* Pull the current clocks from 0x50 also */ + if (conf & (1 << (1 + hwif->channel))) + idev->clock_mode = ATA_50; + else + idev->clock_mode = ATA_66; + + idev->want[0][1] = ATA_ANY; + idev->want[1][1] = ATA_ANY; + + /* + * Not in the docs but according to the reference driver + * this is neccessary. + */ + + pci_read_config_byte(hwif->pci_dev, 0x08, &conf); + if(conf == 0x10) { + idev->timing10 = 1; + hwif->atapi_dma = 0; + if(!idev->smart) + printk(KERN_WARNING "it821x: Revision 0x10, workarounds activated.\n"); + } + + hwif->speedproc = &it821x_tune_chipset; + hwif->tuneproc = &it821x_tuneproc; + + /* MWDMA/PIO clock switching for pass through mode */ + if(!idev->smart) { + hwif->dma_start = &it821x_dma_start; + hwif->ide_dma_end = &it821x_dma_end; + } + + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + goto fallback; + + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + + hwif->ide_dma_check = &it821x_config_drive_for_dma; + if (!(hwif->udma_four)) + hwif->udma_four = ata66_it821x(hwif); + + /* + * The BIOS often doesn't set up DMA on this controller + * so we always do it. + */ + + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + return; +fallback: + hwif->autodma = 0; + return; +} + +static void __devinit it8212_disable_raid(struct pci_dev *dev) +{ + /* Reset local CPU, and set BIOS not ready */ + pci_write_config_byte(dev, 0x5E, 0x01); + + /* Set to bypass mode, and reset PCI bus */ + pci_write_config_byte(dev, 0x50, 0x00); + pci_write_config_word(dev, PCI_COMMAND, + PCI_COMMAND_PARITY | PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config_word(dev, 0x40, 0xA0F3); + + pci_write_config_dword(dev,0x4C, 0x02040204); + pci_write_config_byte(dev, 0x42, 0x36); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0); +} + +static unsigned int __devinit init_chipset_it821x(struct pci_dev *dev, const char *name) +{ + u8 conf; + static char *mode[2] = { "pass through", "smart" }; + + /* Force the card into bypass mode if so requested */ + if (it8212_noraid) { + printk(KERN_INFO "it8212: forcing bypass mode.\n"); + it8212_disable_raid(dev); + } + pci_read_config_byte(dev, 0x50, &conf); + printk(KERN_INFO "it821x: controller in %s mode.\n", mode[conf & 1]); + return 0; +} + + +#define DECLARE_ITE_DEV(name_str) \ + { \ + .name = name_str, \ + .init_chipset = init_chipset_it821x, \ + .init_hwif = init_hwif_it821x, \ + .channels = 2, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + .fixup = it821x_fixups \ + } + +static ide_pci_device_t it821x_chipsets[] __devinitdata = { + /* 0 */ DECLARE_ITE_DEV("IT8212"), +}; + +/** + * it821x_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE821x controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_setup_pci_device(dev, &it821x_chipsets[id->driver_data]); + return 0; +} + +static struct pci_device_id it821x_pci_tbl[] = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it821x_pci_tbl); + +static struct pci_driver driver = { + .name = "ITE821x IDE", + .id_table = it821x_pci_tbl, + .probe = it821x_init_one, +}; + +static int __init it821x_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(it821x_ide_init); + +module_param_named(noraid, it8212_noraid, int, S_IRUGO); +MODULE_PARM_DESC(it8212_noraid, "Force card into bypass mode"); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 821x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index 82a1103b241..c6f5fa4b4ca 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -442,7 +442,7 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha return (dev->irq) ? dev->irq : 0; } -static unsigned int __init ata66_svwks_svwks (ide_hwif_t *hwif) +static unsigned int __devinit ata66_svwks_svwks (ide_hwif_t *hwif) { return 1; } @@ -454,7 +454,7 @@ static unsigned int __init ata66_svwks_svwks (ide_hwif_t *hwif) * Bit 14 clear = primary IDE channel does not have 80-pin cable. * Bit 14 set = primary IDE channel has 80-pin cable. */ -static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) +static unsigned int __devinit ata66_svwks_dell (ide_hwif_t *hwif) { struct pci_dev *dev = hwif->pci_dev; if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && @@ -472,7 +472,7 @@ static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) * * WARNING: this only works on Alpine hardware! */ -static unsigned int __init ata66_svwks_cobalt (ide_hwif_t *hwif) +static unsigned int __devinit ata66_svwks_cobalt (ide_hwif_t *hwif) { struct pci_dev *dev = hwif->pci_dev; if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN && @@ -483,7 +483,7 @@ static unsigned int __init ata66_svwks_cobalt (ide_hwif_t *hwif) return 0; } -static unsigned int __init ata66_svwks (ide_hwif_t *hwif) +static unsigned int __devinit ata66_svwks (ide_hwif_t *hwif) { struct pci_dev *dev = hwif->pci_dev; @@ -573,7 +573,7 @@ static int __devinit init_setup_svwks (struct pci_dev *dev, ide_pci_device_t *d) return ide_setup_pci_device(dev, d); } -static int __init init_setup_csb6 (struct pci_dev *dev, ide_pci_device_t *d) +static int __devinit init_setup_csb6 (struct pci_dev *dev, ide_pci_device_t *d) { if (!(PCI_FUNC(dev->devfn) & 1)) { d->bootable = NEVER_BOARD; diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index c77a82e4605..3e72c9b1461 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -17,11 +17,10 @@ #include <linux/init.h> #include <linux/gameport.h> #include <linux/wait.h> -#include <linux/completion.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/kthread.h> /*#include <asm/io.h>*/ @@ -238,8 +237,7 @@ struct gameport_event { static DEFINE_SPINLOCK(gameport_event_lock); /* protects gameport_event_list */ static LIST_HEAD(gameport_event_list); static DECLARE_WAIT_QUEUE_HEAD(gameport_wait); -static DECLARE_COMPLETION(gameport_exited); -static int gameport_pid; +static struct task_struct *gameport_task; static void gameport_queue_event(void *object, struct module *owner, enum gameport_event_type event_type) @@ -250,12 +248,12 @@ static void gameport_queue_event(void *object, struct module *owner, spin_lock_irqsave(&gameport_event_lock, flags); /* - * Scan event list for the other events for the same gameport port, + * Scan event list for the other events for the same gameport port, * starting with the most recent one. If event is the same we * do not need add new one. If event is of different type we * need to add this event and should not look further because * we need to preseve sequence of distinct events. - */ + */ list_for_each_entry_reverse(event, &gameport_event_list, node) { if (event->object == object) { if (event->type == event_type) @@ -432,20 +430,15 @@ static struct gameport *gameport_get_pending_child(struct gameport *parent) static int gameport_thread(void *nothing) { - lock_kernel(); - daemonize("kgameportd"); - allow_signal(SIGTERM); - do { gameport_handle_events(); - wait_event_interruptible(gameport_wait, !list_empty(&gameport_event_list)); + wait_event_interruptible(gameport_wait, + kthread_should_stop() || !list_empty(&gameport_event_list)); try_to_freeze(); - } while (!signal_pending(current)); + } while (!kthread_should_stop()); printk(KERN_DEBUG "gameport: kgameportd exiting\n"); - - unlock_kernel(); - complete_and_exit(&gameport_exited, 0); + return 0; } @@ -773,9 +766,10 @@ void gameport_close(struct gameport *gameport) static int __init gameport_init(void) { - if (!(gameport_pid = kernel_thread(gameport_thread, NULL, CLONE_KERNEL))) { + gameport_task = kthread_run(gameport_thread, NULL, "kgameportd"); + if (IS_ERR(gameport_task)) { printk(KERN_ERR "gameport: Failed to start kgameportd\n"); - return -1; + return PTR_ERR(gameport_task); } gameport_bus.dev_attrs = gameport_device_attrs; @@ -789,8 +783,7 @@ static int __init gameport_init(void) static void __exit gameport_exit(void) { bus_unregister(&gameport_bus); - kill_proc(gameport_pid, SIGTERM, 1); - wait_for_completion(&gameport_exited); + kthread_stop(gameport_task); } module_init(gameport_init); diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 341824c4852..f367695e69b 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -31,10 +31,9 @@ #include <linux/serio.h> #include <linux/errno.h> #include <linux/wait.h> -#include <linux/completion.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/slab.h> +#include <linux/kthread.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Serio abstraction core"); @@ -43,6 +42,7 @@ MODULE_LICENSE("GPL"); EXPORT_SYMBOL(serio_interrupt); EXPORT_SYMBOL(__serio_register_port); EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_unregister_child_port); EXPORT_SYMBOL(__serio_unregister_port_delayed); EXPORT_SYMBOL(__serio_register_driver); EXPORT_SYMBOL(serio_unregister_driver); @@ -68,6 +68,37 @@ static void serio_destroy_port(struct serio *serio); static void serio_reconnect_port(struct serio *serio); static void serio_disconnect_port(struct serio *serio); +static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) +{ + int retval; + + down(&serio->drv_sem); + retval = drv->connect(serio, drv); + up(&serio->drv_sem); + + return retval; +} + +static int serio_reconnect_driver(struct serio *serio) +{ + int retval = -1; + + down(&serio->drv_sem); + if (serio->drv && serio->drv->reconnect) + retval = serio->drv->reconnect(serio); + up(&serio->drv_sem); + + return retval; +} + +static void serio_disconnect_driver(struct serio *serio) +{ + down(&serio->drv_sem); + if (serio->drv) + serio->drv->disconnect(serio); + up(&serio->drv_sem); +} + static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) { while (ids->type || ids->proto) { @@ -91,7 +122,7 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) if (serio_match_port(drv->id_table, serio)) { serio->dev.driver = &drv->driver; - if (drv->connect(serio, drv)) { + if (serio_connect_driver(serio, drv)) { serio->dev.driver = NULL; goto out; } @@ -138,8 +169,7 @@ struct serio_event { static DEFINE_SPINLOCK(serio_event_lock); /* protects serio_event_list */ static LIST_HEAD(serio_event_list); static DECLARE_WAIT_QUEUE_HEAD(serio_wait); -static DECLARE_COMPLETION(serio_exited); -static int serio_pid; +static struct task_struct *serio_task; static void serio_queue_event(void *object, struct module *owner, enum serio_event_type event_type) @@ -150,12 +180,12 @@ static void serio_queue_event(void *object, struct module *owner, spin_lock_irqsave(&serio_event_lock, flags); /* - * Scan event list for the other events for the same serio port, + * Scan event list for the other events for the same serio port, * starting with the most recent one. If event is the same we * do not need add new one. If event is of different type we * need to add this event and should not look further because * we need to preseve sequence of distinct events. - */ + */ list_for_each_entry_reverse(event, &serio_event_list, node) { if (event->object == object) { if (event->type == event_type) @@ -337,20 +367,15 @@ static struct serio *serio_get_pending_child(struct serio *parent) static int serio_thread(void *nothing) { - lock_kernel(); - daemonize("kseriod"); - allow_signal(SIGTERM); - do { serio_handle_events(); - wait_event_interruptible(serio_wait, !list_empty(&serio_event_list)); + wait_event_interruptible(serio_wait, + kthread_should_stop() || !list_empty(&serio_event_list)); try_to_freeze(); - } while (!signal_pending(current)); + } while (!kthread_should_stop()); printk(KERN_DEBUG "serio: kseriod exiting\n"); - - unlock_kernel(); - complete_and_exit(&serio_exited, 0); + return 0; } @@ -557,7 +582,7 @@ static void serio_destroy_port(struct serio *serio) static void serio_reconnect_port(struct serio *serio) { do { - if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { + if (serio_reconnect_driver(serio)) { serio_disconnect_port(serio); serio_find_driver(serio); /* Ok, old children are now gone, we are done */ @@ -630,6 +655,19 @@ void serio_unregister_port(struct serio *serio) } /* + * Safely unregisters child port if one is present. + */ +void serio_unregister_child_port(struct serio *serio) +{ + down(&serio_sem); + if (serio->child) { + serio_disconnect_port(serio->child); + serio_destroy_port(serio->child); + } + up(&serio_sem); +} + +/* * Submits register request to kseriod for subsequent execution. * Can be used when it is not obvious whether the serio_sem is * taken or not and when delayed execution is feasible. @@ -686,15 +724,14 @@ static int serio_driver_probe(struct device *dev) struct serio *serio = to_serio_port(dev); struct serio_driver *drv = to_serio_driver(dev->driver); - return drv->connect(serio, drv); + return serio_connect_driver(serio, drv); } static int serio_driver_remove(struct device *dev) { struct serio *serio = to_serio_port(dev); - struct serio_driver *drv = to_serio_driver(dev->driver); - drv->disconnect(serio); + serio_disconnect_driver(serio); return 0; } @@ -730,11 +767,9 @@ start_over: static void serio_set_drv(struct serio *serio, struct serio_driver *drv) { - down(&serio->drv_sem); serio_pause_rx(serio); serio->drv = drv; serio_continue_rx(serio); - up(&serio->drv_sem); } static int serio_bus_match(struct device *dev, struct device_driver *drv) @@ -794,7 +829,7 @@ static int serio_resume(struct device *dev) { struct serio *serio = to_serio_port(dev); - if (!serio->drv || !serio->drv->reconnect || serio->drv->reconnect(serio)) { + if (serio_reconnect_driver(serio)) { /* * Driver re-probing can take a while, so better let kseriod * deal with it. @@ -848,9 +883,10 @@ irqreturn_t serio_interrupt(struct serio *serio, static int __init serio_init(void) { - if (!(serio_pid = kernel_thread(serio_thread, NULL, CLONE_KERNEL))) { + serio_task = kthread_run(serio_thread, NULL, "kseriod"); + if (IS_ERR(serio_task)) { printk(KERN_ERR "serio: Failed to start kseriod\n"); - return -1; + return PTR_ERR(serio_task); } serio_bus.dev_attrs = serio_device_attrs; @@ -866,8 +902,7 @@ static int __init serio_init(void) static void __exit serio_exit(void) { bus_unregister(&serio_bus); - kill_proc(serio_pid, SIGTERM, 1); - wait_for_completion(&serio_exited); + kthread_stop(serio_task); } module_init(serio_init); diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index dc00c85e3e3..ee750e9456d 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -486,6 +486,14 @@ static int avmcs_event(event_t event, int priority, return 0; } /* avmcs_event */ +static struct pcmcia_device_id avmcs_ids[] = { + PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335), + PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430), + PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, avmcs_ids); + static struct pcmcia_driver avmcs_driver = { .owner = THIS_MODULE, .drv = { @@ -493,6 +501,7 @@ static struct pcmcia_driver avmcs_driver = { }, .attach = avmcs_attach, .detach = avmcs_detach, + .id_table = avmcs_ids, }; static int __init avmcs_init(void) diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 663a0bf703b..67c60e04a37 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -501,6 +501,13 @@ static int avma1cs_event(event_t event, int priority, return 0; } /* avma1cs_event */ +static struct pcmcia_device_id avma1cs_ids[] = { + PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb), + PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids); + static struct pcmcia_driver avma1cs_driver = { .owner = THIS_MODULE, .drv = { @@ -508,6 +515,7 @@ static struct pcmcia_driver avma1cs_driver = { }, .attach = avma1cs_attach, .detach = avma1cs_detach, + .id_table = avma1cs_ids, }; /*====================================================================*/ diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index bfc013225f4..9146be54704 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -508,6 +508,13 @@ static int elsa_cs_event(event_t event, int priority, return 0; } /* elsa_cs_event */ +static struct pcmcia_device_id elsa_ids[] = { + PCMCIA_DEVICE_PROD_ID12("ELSA AG (Aachen, Germany)", "MicroLink ISDN/MC ", 0x983de2c4, 0x333ba257), + PCMCIA_DEVICE_PROD_ID12("ELSA GmbH, Aachen", "MicroLink ISDN/MC ", 0x639e5718, 0x333ba257), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, elsa_ids); + static struct pcmcia_driver elsa_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -515,6 +522,7 @@ static struct pcmcia_driver elsa_cs_driver = { }, .attach = elsa_cs_attach, .detach = elsa_cs_detach, + .id_table = elsa_ids, }; static int __init init_elsa_cs(void) diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 44965124147..058147a6957 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -616,6 +616,18 @@ static int sedlbauer_event(event_t event, int priority, return 0; } /* sedlbauer_event */ +static struct pcmcia_device_id sedlbauer_ids[] = { + PCMCIA_DEVICE_PROD_ID1234("SEDLBAUER", "speed star II", "V 3.1", "(c) 93 - 98 cb ", 0x81fb79f5, 0xf3612e1d, 0x6b95c78a, 0x50d4149c), + PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D67", 0x81fb79f5, 0xe4e9bc12, 0x397b7e90), + PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D98", 0x81fb79f5, 0xe4e9bc12, 0x2e5c7fce), + PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", " (C) 93-94 VK", 0x81fb79f5, 0xe4e9bc12, 0x8db143fe), + PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", " (c) 93-95 VK", 0x81fb79f5, 0xe4e9bc12, 0xb391ab4c), + PCMCIA_DEVICE_PROD_ID12("HST High Soft Tech GmbH", "Saphir II B", 0xd79e0b84, 0x21d083ae), +/* PCMCIA_DEVICE_PROD_ID1234("SEDLBAUER", 0x81fb79f5), */ /* too generic*/ + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, sedlbauer_ids); + static struct pcmcia_driver sedlbauer_driver = { .owner = THIS_MODULE, .drv = { @@ -623,6 +635,7 @@ static struct pcmcia_driver sedlbauer_driver = { }, .attach = sedlbauer_attach, .detach = sedlbauer_detach, + .id_table = sedlbauer_ids, }; static int __init init_sedlbauer_cs(void) diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index 63e8e20c17a..107376ff5b9 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -489,6 +489,12 @@ static int teles_cs_event(event_t event, int priority, return 0; } /* teles_cs_event */ +static struct pcmcia_device_id teles_ids[] = { + PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, teles_ids); + static struct pcmcia_driver teles_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -496,6 +502,7 @@ static struct pcmcia_driver teles_cs_driver = { }, .attach = teles_attach, .detach = teles_detach, + .id_table = teles_ids, }; static int __init init_teles_cs(void) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8480057eadb..2bea2e0b06f 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -607,6 +607,16 @@ config MTD_PCMCIA cards are usually around 4-16MiB in size. This does not include Compact Flash cards which are treated as IDE devices. +config MTD_PCMCIA_ANONYMOUS + bool "Use PCMCIA MTD drivers for anonymous PCMCIA cards" + depends on MTD_PCMCIA + default N + help + If this option is enabled, PCMCIA cards which do not report + anything about themselves are assumed to be MTD cards. + + If unsure, say N. + config MTD_UCLINUX tristate "Generic uClinux RAM/ROM filesystem support" depends on MTD_PARTITIONS && !MMU diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index e37b4c1976e..c2655a817e3 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -818,6 +818,32 @@ static dev_link_t *pcmciamtd_attach(void) return link; } +static struct pcmcia_device_id pcmciamtd_ids[] = { + PCMCIA_DEVICE_FUNC_ID(1), + PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCS-2M", "2MB SRAM", 0x547e66dc, 0x1fed36cd, 0x36eadd21), + PCMCIA_DEVICE_PROD_ID12("IBM", "2MB SRAM", 0xb569a6e5, 0x36eadd21), + PCMCIA_DEVICE_PROD_ID12("IBM", "4MB FLASH", 0xb569a6e5, 0x8bc54d2a), + PCMCIA_DEVICE_PROD_ID12("IBM", "8MB FLASH", 0xb569a6e5, 0x6df1be3e), + PCMCIA_DEVICE_PROD_ID12("Intel", "S2E20SW", 0x816cc815, 0xd14c9dcf), + PCMCIA_DEVICE_PROD_ID12("Intel", "S2E8 SW", 0x816cc815, 0xa2d7dedb), + PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-02 ", 0x40ade711, 0x145cea5c), + PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-04 ", 0x40ade711, 0x42064dda), + PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-20 ", 0x40ade711, 0x25ee5cb0), + PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8), + PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c), + PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), + PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), + PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), + PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca), + PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944), + /* the following was commented out in pcmcia-cs-3.2.7 */ + /* PCMCIA_DEVICE_PROD_ID12("RATOC Systems,Inc.", "SmartMedia ADAPTER PC Card", 0xf4a2fefe, 0x5885b2ae), */ +#ifdef CONFIG_MTD_PCMCIA_ANONYMOUS + { .match_flags = PCMCIA_DEV_ID_MATCH_ANONYMOUS, }, +#endif + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, pcmciamtd_ids); static struct pcmcia_driver pcmciamtd_driver = { .drv = { @@ -825,7 +851,8 @@ static struct pcmcia_driver pcmciamtd_driver = { }, .attach = pcmciamtd_attach, .detach = pcmciamtd_detach, - .owner = THIS_MODULE + .owner = THIS_MODULE, + .id_table = pcmciamtd_ids, }; diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index c6e8b25f968..f0fc04bd37c 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -1286,6 +1286,13 @@ static int el3_close(struct net_device *dev) return 0; } +static struct pcmcia_device_id tc574_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "3CCFEM556.cis"), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, tc574_ids); + static struct pcmcia_driver tc574_driver = { .owner = THIS_MODULE, .drv = { @@ -1293,6 +1300,7 @@ static struct pcmcia_driver tc574_driver = { }, .attach = tc574_attach, .detach = tc574_detach, + .id_table = tc574_ids, }; static int __init init_tc574(void) diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 89abdda1d34..8fa1b5f0fb6 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -1057,6 +1057,17 @@ static int el3_close(struct net_device *dev) return 0; } +static struct pcmcia_device_id tc589_ids[] = { + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589), + PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "3CXEM556.cis"), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, tc589_ids); + static struct pcmcia_driver tc589_driver = { .owner = THIS_MODULE, .drv = { @@ -1064,6 +1075,7 @@ static struct pcmcia_driver tc589_driver = { }, .attach = tc589_attach, .detach = tc589_detach, + .id_table = tc589_ids, }; static int __init init_tc589(void) diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 853b586e481..23ce77b1d5b 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -850,6 +850,34 @@ static void block_output(struct net_device *dev, int count, outsw(nic_base + AXNET_DATAPORT, buf, count>>1); } +static struct pcmcia_device_id axnet_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x016c, 0x0081), + PCMCIA_DEVICE_MANF_CARD(0x018a, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0303), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0309), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1106), + PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), + PCMCIA_DEVICE_PROD_ID124("Fast Ethernet", "16-bit PC Card", "AX88190", 0xb4be14e3, 0x9a12eb6a, 0xab9be5ef), + PCMCIA_DEVICE_PROD_ID12("ASIX", "AX88190", 0x0959823b, 0xab9be5ef), + PCMCIA_DEVICE_PROD_ID12("Billionton", "LNA-100B", 0x552ab682, 0xbc3b87e1), + PCMCIA_DEVICE_PROD_ID12("CHEETAH ETHERCARD", "EN2228", 0x00fa7bc8, 0x00e990cc), + PCMCIA_DEVICE_PROD_ID12("CNet", "CNF301", 0xbc477dde, 0x78c5f40b), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEther PCC-TXD", 0x5261440f, 0x436768c5), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FEtherII PCC-TXD", 0x5261440f, 0x730df72e), + PCMCIA_DEVICE_PROD_ID12("Dynalink", "L100C16", 0x55632fd5, 0x66bc2a90), + PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V3)", 0x0733cc81, 0x232019a8), + PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC3-TX", 0x481e0094, 0xf91af609), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "100BASE", 0x281f1c5d, 0x7c2add04), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEtherCard", 0x281f1c5d, 0x7ef26116), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FEP501", 0x281f1c5d, 0x2e272058), + PCMCIA_DEVICE_PROD_ID14("Network Everywhere", "AX88190", 0x820a67b6, 0xab9be5ef), + /* this is not specific enough */ + /* PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, axnet_ids); + static struct pcmcia_driver axnet_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -857,6 +885,7 @@ static struct pcmcia_driver axnet_cs_driver = { }, .attach = axnet_attach, .detach = axnet_detach, + .id_table = axnet_ids, }; static int __init init_axnet_cs(void) diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c index 4294e1e3f15..68d58cc58d3 100644 --- a/drivers/net/pcmcia/com20020_cs.c +++ b/drivers/net/pcmcia/com20020_cs.c @@ -483,7 +483,11 @@ static int com20020_event(event_t event, int priority, return 0; } /* com20020_event */ - +static struct pcmcia_device_id com20020_ids[] = { + PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, com20020_ids); static struct pcmcia_driver com20020_cs_driver = { .owner = THIS_MODULE, @@ -492,6 +496,7 @@ static struct pcmcia_driver com20020_cs_driver = { }, .attach = com20020_attach, .detach = com20020_detach, + .id_table = com20020_ids, }; static int __init init_com20020_cs(void) diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 0424865e809..917adbbf0b5 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -435,7 +435,9 @@ static void fmvj18x_config(dev_link_t *link) pcmcia_get_status(handle, &status); if (status.CardState & CS_EVENT_3VCARD) link->conf.Vcc = 33; /* inserted in 3.3V slot */ - } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410) { + } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410 + || le16_to_cpu(buf[1]) == PRODID_TDK_NP9610 + || le16_to_cpu(buf[1]) == PRODID_TDK_MN3200) { /* MultiFunction Card */ link->conf.ConfigBase = 0x800; link->conf.ConfigIndex = 0x47; @@ -764,6 +766,31 @@ static int fmvj18x_event(event_t event, int priority, return 0; } /* fmvj18x_event */ +static struct pcmcia_device_id fmvj18x_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0004, 0x0004), + PCMCIA_DEVICE_PROD_ID12("EAGLE Technology", "NE200 ETHERNET LAN MBH10302 04", 0x528c88c4, 0x74f91e59), + PCMCIA_DEVICE_PROD_ID12("Eiger Labs,Inc", "EPX-10BT PC Card Ethernet 10BT", 0x53af556e, 0x877f9922), + PCMCIA_DEVICE_PROD_ID12("Eiger labs,Inc.", "EPX-10BT PC Card Ethernet 10BT", 0xf47e6c66, 0x877f9922), + PCMCIA_DEVICE_PROD_ID12("FUJITSU", "LAN Card(FMV-J182)", 0x6ee5a3d8, 0x5baf31db), + PCMCIA_DEVICE_PROD_ID12("FUJITSU", "MBH10308", 0x6ee5a3d8, 0x3f04875e), + PCMCIA_DEVICE_PROD_ID12("FUJITSU TOWA", "LA501", 0xb8451188, 0x12939ba2), + PCMCIA_DEVICE_PROD_ID12("HITACHI", "HT-4840-11", 0xf4f43949, 0x773910f4), + PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310B Ver1.0 ", 0x8cef4d3a, 0x075fc7b6), + PCMCIA_DEVICE_PROD_ID12("NextComK.K.", "NC5310 Ver1.0 ", 0x8cef4d3a, 0xbccf43e6), + PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "10BASE_T CARD R280", 0x85c10e17, 0xd9413666), + PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CD02x", 0x1eae9475, 0x8fa0ee70), + PCMCIA_DEVICE_PROD_ID12("TDK", "LAC-CF010", 0x1eae9475, 0x7683bc9a), + PCMCIA_DEVICE_PROD_ID1("CONTEC Co.,Ltd.", 0x58d8fee2), + PCMCIA_DEVICE_PROD_ID1("PCMCIA LAN MBH10304 ES", 0x2599f454), + PCMCIA_DEVICE_PROD_ID1("PCMCIA MBH10302", 0x8f4005da), + PCMCIA_DEVICE_PROD_ID1("UBKK,V2.0", 0x90888080), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, fmvj18x_ids); + static struct pcmcia_driver fmvj18x_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -771,6 +798,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = { }, .attach = fmvj18x_attach, .detach = fmvj18x_detach, + .id_table = fmvj18x_ids, }; static int __init init_fmvj18x_cs(void) diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index f0ff06e2041..cf6d073ea55 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -508,6 +508,13 @@ static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase) return; } +static struct pcmcia_device_id ibmtr_ids[] = { + PCMCIA_DEVICE_PROD_ID12("3Com", "TokenLink Velocity PC Card", 0x41240e5b, 0x82c3734e), + PCMCIA_DEVICE_PROD_ID12("IBM", "TOKEN RING", 0xb569a6e5, 0xbf8eed47), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, ibmtr_ids); + static struct pcmcia_driver ibmtr_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -515,6 +522,7 @@ static struct pcmcia_driver ibmtr_cs_driver = { }, .attach = ibmtr_attach, .detach = ibmtr_detach, + .id_table = ibmtr_ids, }; static int __init init_ibmtr_cs(void) diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 4603807fcaf..b86e7253fbf 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -1675,6 +1675,13 @@ static void set_multicast_list(struct net_device *dev) } /* set_multicast_list */ +static struct pcmcia_device_id nmclan_ids[] = { + PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet", 0x0ebf1d60, 0x00b2e941), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, nmclan_ids); + static struct pcmcia_driver nmclan_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -1682,6 +1689,7 @@ static struct pcmcia_driver nmclan_cs_driver = { }, .attach = nmclan_attach, .detach = nmclan_detach, + .id_table = nmclan_ids, }; static int __init init_nmclan_cs(void) diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index f3ea4a9f2bf..855a45d062b 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -1637,6 +1637,208 @@ failed: /*====================================================================*/ +static struct pcmcia_device_id pcnet_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0057, 0x0021), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0xea15), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0x3341), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0143, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x021b, 0x0101), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "AnyCom", "Fast Ethernet ", 0x578ba6e7, 0x02d92d1e), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), + PCMCIA_MFC_DEVICE_PROD_ID12(0, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), + PCMCIA_MFC_DEVICE_PROD_ID123(0, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), + PCMCIA_MFC_DEVICE_PROD_ID2(0, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), + PCMCIA_DEVICE_MANF_CARD(0x0057, 0x1004), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x000d), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0075), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0145), + PCMCIA_DEVICE_MANF_CARD(0x0149, 0x0230), + PCMCIA_DEVICE_MANF_CARD(0x0149, 0x4530), +/* PCMCIA_DEVICE_MANF_CARD(0x0149, 0xc1ab), conflict with axnet_cs */ + PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0110), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x2328), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x8041), + PCMCIA_DEVICE_MANF_CARD(0x0213, 0x2452), +/* PCMCIA_DEVICE_MANF_CARD(0x021b, 0x0202), conflict with axnet_cs */ + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0300), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0307), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030a), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1103), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1121), + PCMCIA_DEVICE_PROD_ID12("2408LAN", "Ethernet", 0x352fff7f, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID123("Cardwell", "PCMCIA", "ETHERNET", 0x9533672e, 0x281f1c5d, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID123("CNet ", "CN30BC", "ETHERNET", 0x9fe55d3d, 0x85601198, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID123("Digital", "Ethernet", "Adapter", 0x9999ab35, 0x00b2e941, 0x4b0d829e), + PCMCIA_DEVICE_PROD_ID123("Edimax Technology Inc.", "PCMCIA", "Ethernet Card", 0x738a0019, 0x281f1c5d, 0x5e9d92c0), + PCMCIA_DEVICE_PROD_ID123("EFA ", "EFA207", "ETHERNET", 0x3d294be4, 0xeb9aab6c, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID123("I-O DATA", "PCLA", "ETHERNET", 0x1d55d7ec, 0xe4c64d34, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCLATE", "ETHERNET", 0x547e66dc, 0x6b260753, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID123("KingMax Technology Inc.", "EN10-T2", "PCMCIA Ethernet Card", 0x932b7189, 0x699e4436, 0x6f6652e0), + PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2216", 0x281f1c5d, 0xd4cd2f20, 0xb87add82), + PCMCIA_DEVICE_PROD_ID123("PCMCIA", "PCMCIA-ETHERNET-CARD", "UE2620", 0x281f1c5d, 0xd4cd2f20, 0x7d3d83a8), + PCMCIA_DEVICE_PROD_ID1("2412LAN", 0x67f236ab), + PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2212", 0xdfc6b5b2, 0xcb112a11), + PCMCIA_DEVICE_PROD_ID12("ACCTON", "EN2216-PCMCIA-ETHERNET", 0xdfc6b5b2, 0x5542bfff), + PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA100-PCM-T V2 100/10M LAN PC Card", 0xbb7fbdd7, 0xcd91cc68), + PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM", 0xbb7fbdd7, 0x5ba10d49), + PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA100-PCM V2", 0x36634a66, 0xc6d05997), + PCMCIA_DEVICE_PROD_ID12("Allied Telesis, K.K.", "CentreCOM LA-PCM_V2", 0xbb7fBdd7, 0x28e299f8), + PCMCIA_DEVICE_PROD_ID12("Allied Telesis K.K.", "LA-PCM V3", 0x36634a66, 0x62241d96), + PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8010", 0x5070a7f9, 0x82f96e96), + PCMCIA_DEVICE_PROD_ID12("AmbiCom", "AMB8610", 0x5070a7f9, 0x86741224), + PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002", 0x93b15570, 0x75ec3efb), + PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8002T", 0x93b15570, 0x461c5247), + PCMCIA_DEVICE_PROD_ID12("AmbiCom Inc", "AMB8010", 0x93b15570, 0x82f96e96), + PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet", 0x578ba6e7, 0x0a9888c1), + PCMCIA_DEVICE_PROD_ID12("AnyCom", "ECO Ethernet 10/100", 0x578ba6e7, 0x939fedbd), + PCMCIA_DEVICE_PROD_ID12("AROWANA", "PCMCIA Ethernet LAN Card", 0x313adbc8, 0x08d9f190), + PCMCIA_DEVICE_PROD_ID12("ASANTE", "FriendlyNet PC Card", 0x3a7ade0f, 0x41c64504), + PCMCIA_DEVICE_PROD_ID12("Billionton", "LNT-10TB", 0x552ab682, 0xeeb1ba6a), + PCMCIA_DEVICE_PROD_ID12("CF", "10Base-Ethernet", 0x44ebf863, 0x93ae4d79), + PCMCIA_DEVICE_PROD_ID12("CNet", "CN40BC Ethernet", 0xbc477dde, 0xfba775a7), + PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "BASEline PCMCIA 10 MBit Ethernetadapter", 0xfa2e424d, 0xe9190d8a), + PCMCIA_DEVICE_PROD_ID12("COMPU-SHACK", "FASTline PCMCIA 10/100 Fast-Ethernet", 0xfa2e424d, 0x3953d9b9), + PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722), + PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd), + PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9), + PCMCIA_DEVICE_PROD_ID12("Corega,K.K.", "Ethernet LAN Card", 0x110d26d9, 0x9fd2f0a2), + PCMCIA_DEVICE_PROD_ID12("corega,K.K.", "Ethernet LAN Card", 0x9791a90e, 0x9fd2f0a2), + PCMCIA_DEVICE_PROD_ID12("CouplerlessPCMCIA", "100BASE", 0xee5af0ad, 0x7c2add04), + PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-010", 0x77008979, 0x9d8d445d), + PCMCIA_DEVICE_PROD_ID12("CyQ've", "ELA-110E 10/100M LAN Card", 0x77008979, 0xfd184814), + PCMCIA_DEVICE_PROD_ID12("DataTrek.", "NetCard ", 0x5cd66d9d, 0x84697ce0), + PCMCIA_DEVICE_PROD_ID12("Dayna Communications, Inc.", "CommuniCard E", 0x0c629325, 0xb4e7dbaf), + PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100", 0x697403d8, 0xe160b995), + PCMCIA_DEVICE_PROD_ID12("Digicom", "Palladio LAN 10/100 Dongless", 0x697403d8, 0xa6d3b233), + PCMCIA_DEVICE_PROD_ID12("DIGITAL", "DEPCM-XX", 0x69616cb3, 0xe600e76e), + PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-650", 0x1a424a1c, 0xf28c8398), + PCMCIA_DEVICE_PROD_ID12("D-Link", "DE-660", 0x1a424a1c, 0xd9a1d05b), + PCMCIA_DEVICE_PROD_ID12("D-Link", "DFE-650", 0x1a424a1c, 0x0f0073f9), + PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 PC Card", 0x725b842d, 0xf1efee84), + PCMCIA_DEVICE_PROD_ID12("Dual Speed", "10/100 Port Attached PC Card", 0x725b842d, 0x2db1f8e9), + PCMCIA_DEVICE_PROD_ID12("Dynalink", "L10BC", 0x55632fd5, 0xdc65f2b1), + PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10BC", 0x6a26d1cf, 0xdc65f2b1), + PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L10C", 0x6a26d1cf, 0xc4f84efb), + PCMCIA_DEVICE_PROD_ID12("E-CARD", "E-CARD", 0x6701da11, 0x6701da11), + PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet 10BaseT card", 0x53c864c6, 0xedd059f6), + PCMCIA_DEVICE_PROD_ID12("EIGER Labs Inc.", "Ethernet Combo card", 0x53c864c6, 0x929c486c), + PCMCIA_DEVICE_PROD_ID12("Ethernet", "Adapter", 0x00b2e941, 0x4b0d829e), + PCMCIA_DEVICE_PROD_ID12("Ethernet Adapter", "E2000 PCMCIA Ethernet", 0x96767301, 0x71fbbc61), + PCMCIA_DEVICE_PROD_ID12("Ethernet PCMCIA adapter", "EP-210", 0x8dd86181, 0xf2b52517), + PCMCIA_DEVICE_PROD_ID12("Fast Ethernet", "Adapter", 0xb4be14e3, 0x4b0d829e), + PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2000", 0x2a151fac, 0xf00555cb), + PCMCIA_DEVICE_PROD_ID12("Grey Cell", "GCS2220", 0x2a151fac, 0xc1b7e327), + PCMCIA_DEVICE_PROD_ID12("GVC", "NIC-2000p", 0x76e171bd, 0x6eb1c947), + PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "Ethernet", 0xe3736c88, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("IC-CARD", "IC-CARD", 0x60cb09a6, 0x60cb09a6), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCETTX", 0x547e66dc, 0x6fc5459b), + PCMCIA_DEVICE_PROD_ID12("iPort", "10/100 Ethernet Card", 0x56c538d2, 0x11b0ffc0), + PCMCIA_DEVICE_PROD_ID12("KANSAI ELECTRIC CO.,LTD", "KLA-PCM/T", 0xb18dc3b4, 0xcc51a956), + PCMCIA_DEVICE_PROD_ID12("KCI", "PE520 PCMCIA Ethernet Adapter", 0xa89b87d3, 0x1eb88e64), + PCMCIA_DEVICE_PROD_ID12("KINGMAX", "EN10T2T", 0x7bcb459a, 0xa5c81fa5), + PCMCIA_DEVICE_PROD_ID12("Kingston", "KNE-PC2", 0x1128e633, 0xce2a89b3), + PCMCIA_DEVICE_PROD_ID12("Kingston Technology Corp.", "EtheRx PC Card Ethernet Adapter", 0x313c7be3, 0x0afb54a2), + PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-10/100CD", 0x1b7827b2, 0xcda71d1c), + PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDF", 0x1b7827b2, 0xfec71e40), + PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDL/T", 0x1b7827b2, 0x79fba4f7), + PCMCIA_DEVICE_PROD_ID12("Laneed", "LD-CDS", 0x1b7827b2, 0x931afaab), + PCMCIA_DEVICE_PROD_ID12("Linksys", "Combo PCMCIA EthernetCard (EC2T)", 0x0733cc81, 0x32ee8c78), + PCMCIA_DEVICE_PROD_ID12("LINKSYS", "E-CARD", 0xf7cb0b07, 0x6701da11), + PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 Integrated PC Card (PCM100)", 0x0733cc81, 0x453c3f9d), + PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100)", 0x0733cc81, 0x66c5a389), + PCMCIA_DEVICE_PROD_ID12("Linksys", "EtherFast 10/100 PC Card (PCMPC100 V2)", 0x0733cc81, 0x3a3b28e9), + PCMCIA_DEVICE_PROD_ID12("Linksys", "HomeLink Phoneline ", 0x0733cc81, 0x5e07cfa0), + PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN100TX", 0x88fcdeda, 0x6d772737), + PCMCIA_DEVICE_PROD_ID12("Logitec", "LPM-LN20T", 0x88fcdeda, 0x81090922), + PCMCIA_DEVICE_PROD_ID12("LONGSHINE", "PCMCIA Ethernet Card", 0xf866b0b0, 0x6f6652e0), + PCMCIA_DEVICE_PROD_ID12("MACNICA", "ME1-JEIDA", 0x20841b68, 0xaf8a3578), + PCMCIA_DEVICE_PROD_ID12("Macsense", "MPC-10", 0xd830297f, 0xd265c307), + PCMCIA_DEVICE_PROD_ID12("Matsushita Electric Industrial Co.,LTD.", "CF-VEL211", 0x44445376, 0x8ded41d4), + PCMCIA_DEVICE_PROD_ID12("MAXTECH", "PCN2000", 0x78d64bc0, 0xca0ca4b8), + PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-T", 0x481e0094, 0xa2eb0cf3), + PCMCIA_DEVICE_PROD_ID12("MELCO", "LPC2-TX", 0x481e0094, 0x41a6916c), + PCMCIA_DEVICE_PROD_ID12("Microcom C.E.", "Travel Card LAN 10/100", 0x4b91cec7, 0xe70220d6), + PCMCIA_DEVICE_PROD_ID12("Microdyne", "NE4200", 0x2e6da59b, 0x0478e472), + PCMCIA_DEVICE_PROD_ID12("MIDORI ELEC.", "LT-PCMT", 0x648d55c1, 0xbde526c7), + PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover 4100", 0x36e1191f, 0x60c229b9), + PCMCIA_DEVICE_PROD_ID12("National Semiconductor", "InfoMover NE4100", 0x36e1191f, 0xa6617ec8), + PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J12", 0x18df0ba0, 0xbc912d76), + PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA410TX", 0x9aa79dc3, 0x60e5bc0e), + PCMCIA_DEVICE_PROD_ID12("NETGEAR", "FA411", 0x9aa79dc3, 0x40fad875), + PCMCIA_DEVICE_PROD_ID12("Network Everywhere", "Fast Ethernet 10/100 PC Card", 0x820a67b6, 0x31ed1a5f), + PCMCIA_DEVICE_PROD_ID12("NextCom K.K.", "Next Hawk", 0xaedaec74, 0xad050ef1), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100Mbps Ethernet Card", 0x281f1c5d, 0x6e41773b), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet", 0x281f1c5d, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET", 0x281f1c5d, 0x3ff7175b), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet 10BaseT Card", 0x281f1c5d, 0x4de2f6c8), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Card", 0x281f1c5d, 0x5e9d92c0), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Ethernet Combo card", 0x281f1c5d, 0x929c486c), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "ETHERNET V1.0", 0x281f1c5d, 0x4d8817c8), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FastEthernet", 0x281f1c5d, 0xfe871eeb), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast-Ethernet", 0x281f1c5d, 0x45f1f3b4), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "FAST ETHERNET CARD", 0x281f1c5d, 0xec5dbca7), + PCMCIA_DEVICE_PROD_ID12("PCMCIA LAN", "Ethernet", 0x7500e246, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "LNT-10TN", 0x281f1c5d, 0xe707f641), + PCMCIA_DEVICE_PROD_ID12("PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "UE2212", 0x281f1c5d, 0xbf17199b), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", " Ethernet NE2000 Compatible", 0x281f1c5d, 0x42d5d7e1), + PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10baseT 3.3V", 0xebf91155, 0x30074c80), + PCMCIA_DEVICE_PROD_ID12("PRETEC", "Ethernet CompactLAN 10BaseT 3.3V", 0xebf91155, 0x7f5a4f50), + PCMCIA_DEVICE_PROD_ID12("Psion Dacom", "Gold Card Ethernet", 0xf5f025c2, 0x3a30e110), + PCMCIA_DEVICE_PROD_ID12("=RELIA==", "Ethernet", 0xcdd0644a, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("RP", "1625B Ethernet NE2000 Compatible", 0xe3e66e22, 0xb96150df), + PCMCIA_DEVICE_PROD_ID12("RPTI", "EP400 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4a7e2ae0), + PCMCIA_DEVICE_PROD_ID12("RPTI", "EP401 Ethernet NE2000 Compatible", 0xdc6f88fd, 0x4bcbd7fd), + PCMCIA_DEVICE_PROD_ID12("RPTI LTD.", "EP400", 0xc53ac515, 0x81e39388), + PCMCIA_DEVICE_PROD_ID12("SCM", "Ethernet Combo card", 0xbdc3b102, 0x929c486c), + PCMCIA_DEVICE_PROD_ID12("Seiko Epson Corp.", "Ethernet", 0x09928730, 0x00b2e941), + PCMCIA_DEVICE_PROD_ID12("SMC", "EZCard-10-PCMCIA", 0xc4f8b18b, 0xfb21d265), + PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision D", 0xc70a4760, 0x2ade483e), + PCMCIA_DEVICE_PROD_ID12("Socket Communications Inc", "Socket EA PCMCIA LAN Adapter Revision E", 0xc70a4760, 0x5dd978a8), + PCMCIA_DEVICE_PROD_ID12("TDK", "LAK-CD031 for PCMCIA", 0x1eae9475, 0x0ed386fa), + PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE450T", 0x466b05f0, 0x8b74bc4f), + PCMCIA_DEVICE_PROD_ID12("Telecom Device K.K.", "SuperSocket RE550T", 0x466b05f0, 0x33c8db2a), + PCMCIA_DEVICE_PROD_ID13("Hypertec", "EP401", 0x8787bec7, 0xf6e4a31e), + PCMCIA_DEVICE_PROD_ID13("KingMax Technology Inc.", "Ethernet Card", 0x932b7189, 0x5e9d92c0), + PCMCIA_DEVICE_PROD_ID13("LONGSHINE", "EP401", 0xf866b0b0, 0xf6e4a31e), + PCMCIA_DEVICE_PROD_ID13("Xircom", "CFE-10", 0x2e3ee845, 0x22a49f89), + PCMCIA_DEVICE_PROD_ID1("CyQ've 10 Base-T LAN CARD", 0x94faf360), + PCMCIA_DEVICE_PROD_ID1("EP-210 PCMCIA LAN CARD.", 0x8850b4de), + PCMCIA_DEVICE_PROD_ID1("ETHER-C16", 0x06a8514f), + PCMCIA_DEVICE_PROD_ID1("IC-CARD", 0x60cb09a6), + PCMCIA_DEVICE_PROD_ID1("NE2000 Compatible", 0x75b8ad5a), + PCMCIA_DEVICE_PROD_ID2("EN-6200P2", 0xa996d078), + /* too generic! */ + /* PCMCIA_DEVICE_PROD_ID12("PCMCIA", "10/100 Ethernet Card", 0x281f1c5d, 0x11b0ffc0), */ + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(0, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(0, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(0, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0175, 0x0000, "DP83903.cis"), + PCMCIA_DEVICE_CIS_MANF_CARD(0xc00f, 0x0002, "LA-PCM.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("KTI", "PE520 PLUS", 0xad180345, 0x9d58d392, "PE520.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("NDC", "Ethernet", 0x01c43ae1, 0x00b2e941, "NE2K.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("PMX ", "PE-200", 0x34f3f1c8, 0x10b59f8c, "PE-200.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("TAMARACK", "Ethernet", 0xcf434fba, 0x00b2e941, "tamarack.cis"), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, pcnet_ids); + static struct pcmcia_driver pcnet_driver = { .drv = { .name = "pcnet_cs", @@ -1644,6 +1846,7 @@ static struct pcmcia_driver pcnet_driver = { .attach = pcnet_attach, .detach = pcnet_detach, .owner = THIS_MODULE, + .id_table = pcnet_ids, }; static int __init init_pcnet_cs(void) diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 8a5e52c40e4..bc01c88c670 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -2327,6 +2327,38 @@ static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) return rc; } +static struct pcmcia_device_id smc91c92_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023), + PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc), + PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1), + PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5), + PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9), + PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953), + PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a), + PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc), + PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9), + PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d), + /* These conflict with other cards! */ + /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */ + /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids); + static struct pcmcia_driver smc91c92_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -2334,6 +2366,7 @@ static struct pcmcia_driver smc91c92_cs_driver = { }, .attach = smc91c92_attach, .detach = smc91c92_detach, + .id_table = smc91c92_ids, }; static int __init init_smc91c92_cs(void) diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 58177d67ea1..0cd225e1595 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -1983,6 +1983,33 @@ do_stop(struct net_device *dev) return 0; } +static struct pcmcia_device_id xirc2ps_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0089, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0138, 0x110a), + PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), + PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), + PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), + PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), + PCMCIA_PFC_DEVICE_PROD_ID13(0, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), + PCMCIA_PFC_DEVICE_PROD_ID12(0, "Xircom", "CreditCard Ethernet", 0x2e3ee845, 0xc0e778c2), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x010a), + PCMCIA_DEVICE_PROD_ID13("Toshiba Information Systems", "TPCENET", 0x1b3b94fe, 0xf381c1a2), + PCMCIA_DEVICE_PROD_ID13("Xircom", "CE3-10/100", 0x2e3ee845, 0x0ec0ac37), + PCMCIA_DEVICE_PROD_ID13("Xircom", "PS-CE2-10", 0x2e3ee845, 0x947d9073), + PCMCIA_DEVICE_PROD_ID13("Xircom", "R2E-100BTX", 0x2e3ee845, 0x2464a6e3), + PCMCIA_DEVICE_PROD_ID13("Xircom", "RE-10", 0x2e3ee845, 0x3e08d609), + PCMCIA_DEVICE_PROD_ID13("Xircom", "XE2000", 0x2e3ee845, 0xf7188e46), + PCMCIA_DEVICE_PROD_ID12("Compaq", "Ethernet LAN Card", 0x54f7c49c, 0x9fd2f0a2), + PCMCIA_DEVICE_PROD_ID12("Compaq", "Netelligent 10/100 PC Card", 0x54f7c49c, 0xefe96769), + PCMCIA_DEVICE_PROD_ID12("Intel", "EtherExpress(TM) PRO/100 PC Card Mobile Adapter16", 0x816cc815, 0x174397db), + PCMCIA_DEVICE_PROD_ID12("Toshiba", "10/100 Ethernet PC Card", 0x44a09d9c, 0xb44deecf), + /* also matches CFE-10 cards! */ + /* PCMCIA_DEVICE_MANF_CARD(0x0105, 0x010a), */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, xirc2ps_ids); + + static struct pcmcia_driver xirc2ps_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -1990,6 +2017,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = { }, .attach = xirc2ps_attach, .detach = xirc2ps_detach, + .id_table = xirc2ps_ids, }; static int __init diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index fbf53af6cda..f10a9523034 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -559,6 +559,15 @@ static int airo_event(event_t event, int priority, return 0; } /* airo_event */ +static struct pcmcia_device_id airo_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x000a), + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0005), + PCMCIA_DEVICE_MANF_CARD(0x015f, 0x0007), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0007), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, airo_ids); + static struct pcmcia_driver airo_driver = { .owner = THIS_MODULE, .drv = { @@ -566,6 +575,7 @@ static struct pcmcia_driver airo_driver = { }, .attach = airo_attach, .detach = airo_detach, + .id_table = airo_ids, }; static int airo_cs_init(void) diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index a4ed28d9c78..86379d4998a 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -646,6 +646,27 @@ static int atmel_event(event_t event, int priority, } /* atmel_event */ /*====================================================================*/ +static struct pcmcia_device_id atmel_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0620), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0696), + PCMCIA_DEVICE_MANF_CARD(0x01bf, 0x3302), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0007), + PCMCIA_DEVICE_PROD_ID12("11WAVE", "11WP611AL-E", 0x9eb2da1f, 0xc9a0d3f9), + PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C502AR", 0xabda4164, 0x41b37e1f), + PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C504", 0xabda4164, 0x5040670a), + PCMCIA_DEVICE_PROD_ID12("ATMEL", "AT76C504A", 0xabda4164, 0xe15ed87f), + PCMCIA_DEVICE_PROD_ID12("BT", "Voyager 1020 Laptop Adapter", 0xae49b86a, 0x1e957cd5), + PCMCIA_DEVICE_PROD_ID12("CNet", "CNWLC 11Mbps Wireless PC Card V-5", 0xbc477dde, 0x502fae6b), + PCMCIA_DEVICE_PROD_ID12("IEEE 802.11b", "Wireless LAN PC Card", 0x5b878724, 0x122f1df6), + PCMCIA_DEVICE_PROD_ID12("OEM", "11Mbps Wireless LAN PC Card V-3", 0xfea54c90, 0x1c5b0f68), + PCMCIA_DEVICE_PROD_ID12("SMC", "2632W", 0xc4f8b18b, 0x30f38774), + PCMCIA_DEVICE_PROD_ID12("SMC", "2632W-V2", 0xc4f8b18b, 0x172d1377), + PCMCIA_DEVICE_PROD_ID12("Wireless", "PC", 0xa407ecdd, 0x556e4d7e), + PCMCIA_DEVICE_PROD_ID12("WLAN", "802.11b PC CARD", 0x575c516c, 0xb1f6dbc4), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, atmel_ids); + static struct pcmcia_driver atmel_driver = { .owner = THIS_MODULE, .drv = { @@ -653,6 +674,7 @@ static struct pcmcia_driver atmel_driver = { }, .attach = atmel_attach, .detach = atmel_detach, + .id_table = atmel_ids, }; static int atmel_cs_init(void) diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c index 382241e7edb..e12bd75b269 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/net/wireless/netwave_cs.c @@ -1668,6 +1668,12 @@ static int netwave_close(struct net_device *dev) { return 0; } +static struct pcmcia_device_id netwave_ids[] = { + PCMCIA_DEVICE_PROD_ID12("Xircom", "CreditCard Netwave", 0x2e3ee845, 0x54e28a28), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, netwave_ids); + static struct pcmcia_driver netwave_driver = { .owner = THIS_MODULE, .drv = { @@ -1675,6 +1681,7 @@ static struct pcmcia_driver netwave_driver = { }, .attach = netwave_attach, .detach = netwave_detach, + .id_table = netwave_ids, }; static int __init init_netwave_cs(void) diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index 74a8227256a..597c4586d04 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -608,6 +608,56 @@ static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION " (David Gibson <hermes@gibson.dropbear.id.au>, " "Pavel Roskin <proski@gnu.org>, et al)"; +static struct pcmcia_device_id orinoco_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), + PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), + PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), + PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), + PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), + PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), + PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), + PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3), + PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0), + PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5), + PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169), + PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3), + PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9), + PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac), + PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab), + PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3), + PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c), + PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18), + PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410), + PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3), + PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01), + PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1), + PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9), + PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26), + PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b), + PCMCIA_DEVICE_PROD_ID1("Symbol Technologies", 0x3f02b4d6), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids); + static struct pcmcia_driver orinoco_driver = { .owner = THIS_MODULE, .drv = { @@ -615,6 +665,7 @@ static struct pcmcia_driver orinoco_driver = { }, .attach = orinoco_cs_attach, .detach = orinoco_cs_detach, + .id_table = orinoco_cs_ids, }; static int __init diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 6e5bda56b8f..31652af52ea 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2904,6 +2904,12 @@ static int write_int(struct file *file, const char __user *buffer, unsigned long } #endif +static struct pcmcia_device_id ray_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x01a6, 0x0000), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, ray_ids); + static struct pcmcia_driver ray_driver = { .owner = THIS_MODULE, .drv = { @@ -2911,6 +2917,7 @@ static struct pcmcia_driver ray_driver = { }, .attach = ray_attach, .detach = ray_detach, + .id_table = ray_ids, }; static int __init init_ray_cs(void) diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 5914b637c0a..fe54571bc12 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -4895,6 +4895,15 @@ wavelan_event(event_t event, /* The event received */ return 0; } +static struct pcmcia_device_id wavelan_ids[] = { + PCMCIA_DEVICE_PROD_ID12("AT&T","WaveLAN/PCMCIA", 0xe7c5affd, 0x1bc50975), + PCMCIA_DEVICE_PROD_ID12("Digital", "RoamAbout/DS", 0x9999ab35, 0x00d05e06), + PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/PCMCIA", 0x23eb9949, 0x1bc50975), + PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/PCMCIA", 0x24358cd4, 0x1bc50975), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, wavelan_ids); + static struct pcmcia_driver wavelan_driver = { .owner = THIS_MODULE, .drv = { @@ -4902,6 +4911,7 @@ static struct pcmcia_driver wavelan_driver = { }, .attach = wavelan_attach, .detach = wavelan_detach, + .id_table = wavelan_ids, }; static int __init diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 0e3afab6275..77d8ac85803 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -2240,6 +2240,12 @@ static int wl3501_event(event_t event, int pri, event_callback_args_t *args) return 0; } +static struct pcmcia_device_id wl3501_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0001), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, wl3501_ids); + static struct pcmcia_driver wl3501_driver = { .owner = THIS_MODULE, .drv = { @@ -2247,6 +2253,7 @@ static struct pcmcia_driver wl3501_driver = { }, .attach = wl3501_attach, .detach = wl3501_detach, + .id_table = wl3501_ids, }; static int __init wl3501_init_module(void) diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index a3fa8185af2..ff45662c4f7 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -373,6 +373,13 @@ int parport_event(event_t event, int priority, return 0; } /* parport_event */ +static struct pcmcia_device_id parport_ids[] = { + PCMCIA_DEVICE_FUNC_ID(3), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0003), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, parport_ids); + static struct pcmcia_driver parport_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -380,6 +387,8 @@ static struct pcmcia_driver parport_cs_driver = { }, .attach = parport_attach, .detach = parport_detach, + .id_table = parport_ids, + }; static int __init init_parport_cs(void) diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 14e4124e152..52ea3459436 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -14,8 +14,8 @@ config PCCARD Say Y here if you want to attach PCMCIA- or PC-cards to your Linux computer. These are credit-card size devices such as network cards, modems or hard drives often used with laptops computers. There are - actually two varieties of these cards: the older 16 bit PCMCIA cards - and the newer 32 bit CardBus cards. + actually two varieties of these cards: 16 bit PCMCIA and 32 bit + CardBus cards. To compile this driver as modules, choose M here: the module will be called pcmcia_core. @@ -42,22 +42,51 @@ config PCMCIA_DEBUG config PCMCIA tristate "16-bit PCMCIA support" + select CRC32 default y ---help--- This option enables support for 16-bit PCMCIA cards. Most older PC-cards are such 16-bit PCMCIA cards, so unless you know you're only using 32-bit CardBus cards, say Y or M here. - To use 16-bit PCMCIA cards, you will need supporting software from - David Hinds' pcmcia-cs package (see the file <file:Documentation/Changes> - for location). Please also read the PCMCIA-HOWTO, available from - <http://www.tldp.org/docs.html#howto>. + To use 16-bit PCMCIA cards, you will need supporting software in + most cases. (see the file <file:Documentation/Changes> for + location and details). To compile this driver as modules, choose M here: the module will be called pcmcia. If unsure, say Y. +config PCMCIA_LOAD_CIS + bool "Load CIS updates from userspace (EXPERIMENTAL)" + depends on PCMCIA && EXPERIMENTAL + select FW_LOADER + default y + help + Some PCMCIA cards require an updated Card Information Structure (CIS) + to be loaded from userspace to work correctly. If you say Y here, + and your userspace is arranged correctly, this will be loaded + automatically using the in-kernel firmware loader and the hotplug + subsystem, instead of relying on cardmgr from pcmcia-cs to do so. + + If unsure, say Y. + +config PCMCIA_IOCTL + bool + depends on PCMCIA + default y + help + If you say Y here, the deprecated ioctl interface to the PCMCIA + subsystem will be built. It is needed by cardmgr and cardctl + (pcmcia-cs) to function properly. + + If you do not use the new pcmciautils package, and have a + yenta, Cirrus PD6729, i82092, i82365 or tcic compatible bridge, + you need to say Y here to be able to use 16-bit PCMCIA cards. + + If unsure, say Y. + config CARDBUS bool "32-bit CardBus support" depends on PCI @@ -77,8 +106,6 @@ comment "PC-card bridges" config YENTA tristate "CardBus yenta-compatible bridge support" - depends on PCI -#fixme: remove dependendcy on CARDBUS depends on CARDBUS select PCCARD_NONSTATIC ---help--- diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 50c29361bc5..ef694c74dfb 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -10,7 +10,8 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_compat.o +pcmcia-y += ds.o pcmcia_compat.o pcmcia_resource.o +pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o obj-$(CONFIG_PCCARD_NONSTATIC) += rsrc_nonstatic.o diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index e29a6ddf2fd..dd7651ff5b4 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -89,8 +89,10 @@ static void __iomem * set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) { pccard_mem_map *mem = &s->cis_mem; + int ret; + if (!(s->features & SS_CAP_STATIC_MAP) && mem->res == NULL) { - mem->res = find_mem_region(0, s->map_size, s->map_size, 0, s); + mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); if (mem->res == NULL) { printk(KERN_NOTICE "cs: unable to map card memory!\n"); return NULL; @@ -99,7 +101,12 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag } mem->card_start = card_offset; mem->flags = flags; - s->ops->set_mem_map(s, mem); + ret = s->ops->set_mem_map(s, mem); + if (ret) { + iounmap(s->cis_virt); + return NULL; + } + if (s->features & SS_CAP_STATIC_MAP) { if (s->cis_virt) iounmap(s->cis_virt); @@ -119,13 +126,13 @@ set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flag #define IS_ATTR 1 #define IS_INDIRECT 8 -int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, +int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { void __iomem *sys, *end; unsigned char *buf = ptr; - cs_dbg(s, 3, "read_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -182,14 +189,16 @@ int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0; } +EXPORT_SYMBOL(pcmcia_read_cis_mem); + -void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, +void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { void __iomem *sys, *end; unsigned char *buf = ptr; - cs_dbg(s, 3, "write_cis_mem(%d, %#x, %u)\n", attr, addr, len); + cs_dbg(s, 3, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len); if (attr & IS_INDIRECT) { /* Indirect accesses use a bunch of special registers at fixed @@ -239,6 +248,8 @@ void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, } } } +EXPORT_SYMBOL(pcmcia_write_cis_mem); + /*====================================================================== @@ -274,7 +285,7 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, ret = read_cb_mem(s, attr, addr, len, ptr); else #endif - ret = read_cis_mem(s, attr, addr, len, ptr); + ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr); if (ret == 0) { /* Copy data into the cache */ @@ -348,7 +359,7 @@ int verify_cis_cache(struct pcmcia_socket *s) read_cb_mem(s, cis->attr, cis->addr, len, buf); else #endif - read_cis_mem(s, cis->attr, cis->addr, len, buf); + pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf); if (memcmp(buf, cis->cache, len) != 0) { kfree(buf); @@ -381,6 +392,7 @@ int pcmcia_replace_cis(struct pcmcia_socket *s, cisdump_t *cis) memcpy(s->fake_cis, cis->Data, cis->Length); return CS_SUCCESS; } +EXPORT_SYMBOL(pcmcia_replace_cis); /*====================================================================== diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 48e4f04530d..e82859d3227 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -43,36 +43,11 @@ #include <pcmcia/ds.h> #include "cs_internal.h" -#ifdef CONFIG_PCI -#define PCI_OPT " [pci]" -#else -#define PCI_OPT "" -#endif -#ifdef CONFIG_CARDBUS -#define CB_OPT " [cardbus]" -#else -#define CB_OPT "" -#endif -#ifdef CONFIG_PM -#define PM_OPT " [pm]" -#else -#define PM_OPT "" -#endif -#if !defined(CONFIG_CARDBUS) && !defined(CONFIG_PCI) && !defined(CONFIG_PM) -#define OPTIONS " none" -#else -#define OPTIONS PCI_OPT CB_OPT PM_OPT -#endif - -static const char *release = "Linux Kernel Card Services"; -static const char *options = "options: " OPTIONS; - -/*====================================================================*/ /* Module parameters */ MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("Linux Kernel Card Services\noptions:" OPTIONS); +MODULE_DESCRIPTION("Linux Kernel Card Services"); MODULE_LICENSE("GPL"); #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0444) @@ -89,9 +64,6 @@ INT_MODULE_PARM(unreset_limit, 30); /* unreset_check's */ /* Access speed for attribute memory windows */ INT_MODULE_PARM(cis_speed, 300); /* ns */ -/* Access speed for IO windows */ -INT_MODULE_PARM(io_speed, 0); /* ns */ - #ifdef DEBUG static int pc_debug; @@ -103,34 +75,26 @@ int cs_debug_level(int level) } #endif -/*====================================================================*/ socket_state_t dead_socket = { .csc_mask = SS_DETECT, }; +EXPORT_SYMBOL(dead_socket); /* List of all sockets, protected by a rwsem */ LIST_HEAD(pcmcia_socket_list); -DECLARE_RWSEM(pcmcia_socket_list_rwsem); EXPORT_SYMBOL(pcmcia_socket_list); -EXPORT_SYMBOL(pcmcia_socket_list_rwsem); - -#ifdef CONFIG_PCMCIA_PROBE -/* mask ofIRQs already reserved by other cards, we should avoid using them */ -static u8 pcmcia_used_irq[NR_IRQS]; -#endif +DECLARE_RWSEM(pcmcia_socket_list_rwsem); +EXPORT_SYMBOL(pcmcia_socket_list_rwsem); -/*==================================================================== - - Low-level PC Card interface drivers need to register with Card - Services using these calls. - -======================================================================*/ /** - * socket drivers are expected to use the following callbacks in their + * Low-level PCMCIA socket drivers need to register with the PCCard + * core using pcmcia_register_socket. + * + * socket drivers are expected to use the following callbacks in their * .drv struct: * - pcmcia_socket_dev_suspend * - pcmcia_socket_dev_resume @@ -230,8 +194,8 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) } /* try to obtain a socket number [yes, it gets ugly if we - * register more than 2^sizeof(unsigned int) pcmcia - * sockets... but the socket number is deprecated + * register more than 2^sizeof(unsigned int) pcmcia + * sockets... but the socket number is deprecated * anyways, so I don't care] */ down_write(&pcmcia_socket_list_rwsem); if (list_empty(&pcmcia_socket_list)) @@ -340,54 +304,49 @@ struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr) EXPORT_SYMBOL(pcmcia_get_socket_by_nr); -/*====================================================================== - - socket_setup() and shutdown_socket() are called by the main event - handler when card insertion and removal events are received. - socket_setup() turns on socket power and resets the socket, in two stages. - shutdown_socket() unconfigures a socket and turns off socket power. - -======================================================================*/ - +/** + * socket_setup() and shutdown_socket() are called by the main event + * handler when card insertion and removal events are received. + * socket_setup() turns on socket power and resets the socket, in two stages. + * shutdown_socket() unconfigures a socket and turns off socket power. + */ static void shutdown_socket(struct pcmcia_socket *s) { - cs_dbg(s, 1, "shutdown_socket\n"); - - /* Blank out the socket state */ - s->socket = dead_socket; - s->ops->init(s); - s->ops->set_socket(s, &s->socket); - s->irq.AssignedIRQ = s->irq.Config = 0; - s->lock_count = 0; - destroy_cis_cache(s); + cs_dbg(s, 1, "shutdown_socket\n"); + + /* Blank out the socket state */ + s->socket = dead_socket; + s->ops->init(s); + s->ops->set_socket(s, &s->socket); + s->irq.AssignedIRQ = s->irq.Config = 0; + s->lock_count = 0; + destroy_cis_cache(s); #ifdef CONFIG_CARDBUS - cb_free(s); + cb_free(s); #endif - s->functions = 0; - if (s->config) { - kfree(s->config); - s->config = NULL; - } - - { - int status; - s->ops->get_status(s, &status); - if (status & SS_POWERON) { - printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s); + s->functions = 0; + if (s->config) { + kfree(s->config); + s->config = NULL; } - } -} /* shutdown_socket */ -/*====================================================================== + { + int status; + s->ops->get_status(s, &status); + if (status & SS_POWERON) { + printk(KERN_ERR "PCMCIA: socket %p: *** DANGER *** unable to remove socket power\n", s); + } + } +} /* shutdown_socket */ - The central event handler. Send_event() sends an event to the - 16-bit subsystem, which then calls the relevant device drivers. - Parse_events() interprets the event bits from - a card status change report. Do_shutdown() handles the high - priority stuff associated with a card removal. - -======================================================================*/ +/** + * The central event handler. Send_event() sends an event to the + * 16-bit subsystem, which then calls the relevant device drivers. + * Parse_events() interprets the event bits from + * a card status change report. Do_shutdown() handles the high + * priority stuff associated with a card removal. + */ /* NOTE: send_event needs to be called with skt->sem held. */ @@ -746,420 +705,9 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events) wake_up(&s->thread_wait); } } /* pcmcia_parse_events */ +EXPORT_SYMBOL(pcmcia_parse_events); -/*====================================================================== - - Special stuff for managing IO windows, because they are scarce. - -======================================================================*/ - -static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, - ioaddr_t num, u_int lines) -{ - int i; - kio_addr_t try, align; - - align = (*base) ? (lines ? 1<<lines : 0) : 1; - if (align && (align < num)) { - if (*base) { - cs_dbg(s, 0, "odd IO request: num %#x align %#lx\n", - num, align); - align = 0; - } else - while (align && (align < num)) align <<= 1; - } - if (*base & ~(align-1)) { - cs_dbg(s, 0, "odd IO request: base %#x align %#lx\n", - *base, align); - align = 0; - } - if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { - *base = s->io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - what was asked for. It is a hack because it does not catch all - potential conflicts, just the most obvious ones. */ - for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].NumPorts != 0) && - ((s->io[i].BasePort & (align-1)) == *base)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) { - s->io[i].res = find_io_region(*base, num, align, s); - if (s->io[i].res) { - s->io[i].Attributes = attr; - s->io[i].BasePort = *base = s->io[i].res->start; - s->io[i].NumPorts = s->io[i].InUse = num; - break; - } else - return 1; - } else if (s->io[i].Attributes != attr) - continue; - /* Try to extend top of window */ - try = s->io[i].BasePort + s->io[i].NumPorts; - if ((*base == 0) || (*base == try)) - if (adjust_io_region(s->io[i].res, s->io[i].res->start, - s->io[i].res->end + num, s) == 0) { - *base = try; - s->io[i].NumPorts += num; - s->io[i].InUse += num; - break; - } - /* Try to extend bottom of window */ - try = s->io[i].BasePort - num; - if ((*base == 0) || (*base == try)) - if (adjust_io_region(s->io[i].res, s->io[i].res->start - num, - s->io[i].res->end, s) == 0) { - s->io[i].BasePort = *base = try; - s->io[i].NumPorts += num; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); -} /* alloc_io_space */ - -static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, - ioaddr_t num) -{ - int i; - - for (i = 0; i < MAX_IO_WIN; i++) { - if ((s->io[i].BasePort <= base) && - (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { - s->io[i].InUse -= num; - /* Free the window if no one else is using it */ - if (s->io[i].InUse == 0) { - s->io[i].NumPorts = 0; - release_resource(s->io[i].res); - kfree(s->io[i].res); - s->io[i].res = NULL; - } - } - } -} - -/*====================================================================== - - Access_configuration_register() reads and writes configuration - registers in attribute memory. Memory window 0 is reserved for - this and the tuple reading services. - -======================================================================*/ - -int pccard_access_configuration_register(struct pcmcia_socket *s, - unsigned int function, - conf_reg_t *reg) -{ - config_t *c; - int addr; - u_char val; - - if (!s || !s->config) - return CS_NO_CARD; - - c = &s->config[function]; - - if (c == NULL) - return CS_NO_CARD; - - if (!(c->state & CONFIG_LOCKED)) - return CS_CONFIGURATION_LOCKED; - - addr = (c->ConfigBase + reg->Offset) >> 1; - - switch (reg->Action) { - case CS_READ: - read_cis_mem(s, 1, addr, 1, &val); - reg->Value = val; - break; - case CS_WRITE: - val = reg->Value; - write_cis_mem(s, 1, addr, 1, &val); - break; - default: - return CS_BAD_ARGS; - break; - } - return CS_SUCCESS; -} /* access_configuration_register */ -EXPORT_SYMBOL(pccard_access_configuration_register); - - -/*====================================================================*/ - -int pccard_get_configuration_info(struct pcmcia_socket *s, - unsigned int function, - config_info_t *config) -{ - config_t *c; - - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - config->Function = function; - -#ifdef CONFIG_CARDBUS - if (s->state & SOCKET_CARDBUS) { - memset(config, 0, sizeof(config_info_t)); - config->Vcc = s->socket.Vcc; - config->Vpp1 = config->Vpp2 = s->socket.Vpp; - config->Option = s->cb_dev->subordinate->number; - if (s->state & SOCKET_CARDBUS_CONFIG) { - config->Attributes = CONF_VALID_CLIENT; - config->IntType = INT_CARDBUS; - config->AssignedIRQ = s->irq.AssignedIRQ; - if (config->AssignedIRQ) - config->Attributes |= CONF_ENABLE_IRQ; - config->BasePort1 = s->io[0].BasePort; - config->NumPorts1 = s->io[0].NumPorts; - } - return CS_SUCCESS; - } -#endif - - c = (s->config != NULL) ? &s->config[function] : NULL; - - if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { - config->Attributes = 0; - config->Vcc = s->socket.Vcc; - config->Vpp1 = config->Vpp2 = s->socket.Vpp; - return CS_SUCCESS; - } - - /* !!! This is a hack !!! */ - memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); - config->Attributes |= CONF_VALID_CLIENT; - config->CardValues = c->CardValues; - config->IRQAttributes = c->irq.Attributes; - config->AssignedIRQ = s->irq.AssignedIRQ; - config->BasePort1 = c->io.BasePort1; - config->NumPorts1 = c->io.NumPorts1; - config->Attributes1 = c->io.Attributes1; - config->BasePort2 = c->io.BasePort2; - config->NumPorts2 = c->io.NumPorts2; - config->Attributes2 = c->io.Attributes2; - config->IOAddrLines = c->io.IOAddrLines; - - return CS_SUCCESS; -} /* get_configuration_info */ -EXPORT_SYMBOL(pccard_get_configuration_info); - -/*====================================================================== - - Return information about this version of Card Services. - -======================================================================*/ - -int pcmcia_get_card_services_info(servinfo_t *info) -{ - unsigned int socket_count = 0; - struct list_head *tmp; - info->Signature[0] = 'C'; - info->Signature[1] = 'S'; - down_read(&pcmcia_socket_list_rwsem); - list_for_each(tmp, &pcmcia_socket_list) - socket_count++; - up_read(&pcmcia_socket_list_rwsem); - info->Count = socket_count; - info->Revision = CS_RELEASE_CODE; - info->CSLevel = 0x0210; - info->VendorString = (char *)release; - return CS_SUCCESS; -} /* get_card_services_info */ - - -/*====================================================================*/ - -int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req) -{ - window_t *win; - int w; - - if (!s || !(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - for (w = idx; w < MAX_WIN; w++) - if (s->state & SOCKET_WIN_REQ(w)) break; - if (w == MAX_WIN) - return CS_NO_MORE_ITEMS; - win = &s->win[w]; - req->Base = win->ctl.res->start; - req->Size = win->ctl.res->end - win->ctl.res->start + 1; - req->AccessSpeed = win->ctl.speed; - req->Attributes = 0; - if (win->ctl.flags & MAP_ATTRIB) - req->Attributes |= WIN_MEMORY_TYPE_AM; - if (win->ctl.flags & MAP_ACTIVE) - req->Attributes |= WIN_ENABLE; - if (win->ctl.flags & MAP_16BIT) - req->Attributes |= WIN_DATA_WIDTH_16; - if (win->ctl.flags & MAP_USE_WAIT) - req->Attributes |= WIN_USE_WAIT; - *handle = win; - return CS_SUCCESS; -} /* get_window */ -EXPORT_SYMBOL(pcmcia_get_window); - -/*===================================================================== - - Return the PCI device associated with a card.. - -======================================================================*/ - -#ifdef CONFIG_CARDBUS - -struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) -{ - if (!s || !(s->state & SOCKET_CARDBUS)) - return NULL; - - return s->cb_dev->subordinate; -} - -EXPORT_SYMBOL(pcmcia_lookup_bus); - -#endif - -/*====================================================================== - - Get the current socket state bits. We don't support the latched - SocketState yet: I haven't seen any point for it. - -======================================================================*/ - -int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status) -{ - config_t *c; - int val; - - s->ops->get_status(s, &val); - status->CardState = status->SocketState = 0; - status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0; - status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0; - status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0; - status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0; - if (s->state & SOCKET_SUSPEND) - status->CardState |= CS_EVENT_PM_SUSPEND; - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - c = (s->config != NULL) ? &s->config[function] : NULL; - if ((c != NULL) && (c->state & CONFIG_LOCKED) && - (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { - u_char reg; - if (c->Present & PRESENT_PIN_REPLACE) { - read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); - status->CardState |= - (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; - status->CardState |= - (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0; - status->CardState |= - (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0; - status->CardState |= - (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0; - } else { - /* No PRR? Then assume we're always ready */ - status->CardState |= CS_EVENT_READY_CHANGE; - } - if (c->Present & PRESENT_EXT_STATUS) { - read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); - status->CardState |= - (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; - } - return CS_SUCCESS; - } - status->CardState |= - (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0; - status->CardState |= - (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0; - status->CardState |= - (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0; - status->CardState |= - (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; - return CS_SUCCESS; -} /* get_status */ -EXPORT_SYMBOL(pccard_get_status); - -/*====================================================================== - - Change the card address of an already open memory window. - -======================================================================*/ - -int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) -{ - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - req->Page = 0; - req->CardOffset = win->ctl.card_start; - return CS_SUCCESS; -} /* get_mem_page */ - -int pcmcia_map_mem_page(window_handle_t win, memreq_t *req) -{ - struct pcmcia_socket *s; - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - if (req->Page != 0) - return CS_BAD_PAGE; - s = win->sock; - win->ctl.card_start = req->CardOffset; - if (s->ops->set_mem_map(s, &win->ctl) != 0) - return CS_BAD_OFFSET; - return CS_SUCCESS; -} /* map_mem_page */ - -/*====================================================================== - - Modify a locked socket configuration - -======================================================================*/ - -int pcmcia_modify_configuration(client_handle_t handle, - modconf_t *mod) -{ - struct pcmcia_socket *s; - config_t *c; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); c = CONFIG(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - if (!(c->state & CONFIG_LOCKED)) - return CS_CONFIGURATION_LOCKED; - - if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { - if (mod->Attributes & CONF_ENABLE_IRQ) { - c->Attributes |= CONF_ENABLE_IRQ; - s->socket.io_irq = s->irq.AssignedIRQ; - } else { - c->Attributes &= ~CONF_ENABLE_IRQ; - s->socket.io_irq = 0; - } - s->ops->set_socket(s, &s->socket); - } - - if (mod->Attributes & CONF_VCC_CHANGE_VALID) - return CS_BAD_VCC; - - /* We only allow changing Vpp1 and Vpp2 to the same value */ - if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && - (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { - if (mod->Vpp1 != mod->Vpp2) - return CS_BAD_VPP; - c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1; - if (s->ops->set_socket(s, &s->socket)) - return CS_BAD_VPP; - } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || - (mod->Attributes & CONF_VPP2_CHANGE_VALID)) - return CS_BAD_VPP; - - return CS_SUCCESS; -} /* modify_configuration */ - /* register pcmcia_callback */ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) { @@ -1188,543 +736,16 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) } EXPORT_SYMBOL(pccard_register_pcmcia); -/*====================================================================*/ -int pcmcia_release_configuration(client_handle_t handle) -{ - pccard_io_map io = { 0, 0, 0, 0, 1 }; - struct pcmcia_socket *s; - int i; - - if (CHECK_HANDLE(handle) || - !(handle->state & CLIENT_CONFIG_LOCKED)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_CONFIG_LOCKED; - s = SOCKET(handle); - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_SUCCESS; -#endif - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (--(s->lock_count) == 0) { - s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */ - s->socket.Vpp = 0; - s->socket.io_irq = 0; - s->ops->set_socket(s, &s->socket); - } - if (c->state & CONFIG_IO_REQ) - for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) - continue; - s->io[i].Config--; - if (s->io[i].Config != 0) - continue; - io.map = i; - s->ops->set_io_map(s, &io); - } - c->state &= ~CONFIG_LOCKED; - } - - return CS_SUCCESS; -} /* release_configuration */ - -/*====================================================================== - - Release_io() releases the I/O ranges allocated by a client. This - may be invoked some time after a card ejection has already dumped - the actual socket configuration, so if the client is "stale", we - don't bother checking the port ranges against the current socket - values. - -======================================================================*/ - -int pcmcia_release_io(client_handle_t handle, io_req_t *req) -{ - struct pcmcia_socket *s; - - if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_IO_REQ; - s = SOCKET(handle); - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_SUCCESS; -#endif - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if ((c->io.BasePort1 != req->BasePort1) || - (c->io.NumPorts1 != req->NumPorts1) || - (c->io.BasePort2 != req->BasePort2) || - (c->io.NumPorts2 != req->NumPorts2)) - return CS_BAD_ARGS; - c->state &= ~CONFIG_IO_REQ; - } - - release_io_space(s, req->BasePort1, req->NumPorts1); - if (req->NumPorts2) - release_io_space(s, req->BasePort2, req->NumPorts2); - - return CS_SUCCESS; -} /* release_io */ - -/*====================================================================*/ - -int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ)) - return CS_BAD_HANDLE; - handle->state &= ~CLIENT_IRQ_REQ; - s = SOCKET(handle); - - if (!(handle->state & CLIENT_STALE)) { - config_t *c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->irq.Attributes != req->Attributes) - return CS_BAD_ATTRIBUTE; - if (s->irq.AssignedIRQ != req->AssignedIRQ) - return CS_BAD_IRQ; - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } - } - - if (req->Attributes & IRQ_HANDLE_PRESENT) { - free_irq(req->AssignedIRQ, req->Instance); - } - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[req->AssignedIRQ]--; -#endif - - return CS_SUCCESS; -} /* cs_release_irq */ - -/*====================================================================*/ - -int pcmcia_release_window(window_handle_t win) -{ - struct pcmcia_socket *s; - - if ((win == NULL) || (win->magic != WINDOW_MAGIC)) - return CS_BAD_HANDLE; - s = win->sock; - if (!(win->handle->state & CLIENT_WIN_REQ(win->index))) - return CS_BAD_HANDLE; - - /* Shut down memory window */ - win->ctl.flags &= ~MAP_ACTIVE; - s->ops->set_mem_map(s, &win->ctl); - s->state &= ~SOCKET_WIN_REQ(win->index); - - /* Release system memory */ - if (win->ctl.res) { - release_resource(win->ctl.res); - kfree(win->ctl.res); - win->ctl.res = NULL; - } - win->handle->state &= ~CLIENT_WIN_REQ(win->index); - - win->magic = 0; - - return CS_SUCCESS; -} /* release_window */ - -/*====================================================================*/ - -int pcmcia_request_configuration(client_handle_t handle, - config_req_t *req) -{ - int i; - u_int base; - struct pcmcia_socket *s; - config_t *c; - pccard_io_map iomap; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - -#ifdef CONFIG_CARDBUS - if (handle->state & CLIENT_CARDBUS) - return CS_UNSUPPORTED_MODE; -#endif - - if (req->IntType & INT_CARDBUS) - return CS_UNSUPPORTED_MODE; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - - /* Do power control. We don't allow changes in Vcc. */ - if (s->socket.Vcc != req->Vcc) - return CS_BAD_VCC; - if (req->Vpp1 != req->Vpp2) - return CS_BAD_VPP; - s->socket.Vpp = req->Vpp1; - if (s->ops->set_socket(s, &s->socket)) - return CS_BAD_VPP; - - c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; - - /* Pick memory or I/O card, DMA mode, interrupt */ - c->IntType = req->IntType; - c->Attributes = req->Attributes; - if (req->IntType & INT_MEMORY_AND_IO) - s->socket.flags |= SS_IOCARD; - if (req->IntType & INT_ZOOMED_VIDEO) - s->socket.flags |= SS_ZVCARD | SS_IOCARD; - if (req->Attributes & CONF_ENABLE_DMA) - s->socket.flags |= SS_DMA_MODE; - if (req->Attributes & CONF_ENABLE_SPKR) - s->socket.flags |= SS_SPKR_ENA; - if (req->Attributes & CONF_ENABLE_IRQ) - s->socket.io_irq = s->irq.AssignedIRQ; - else - s->socket.io_irq = 0; - s->ops->set_socket(s, &s->socket); - s->lock_count++; - - /* Set up CIS configuration registers */ - base = c->ConfigBase = req->ConfigBase; - c->Present = c->CardValues = req->Present; - if (req->Present & PRESENT_COPY) { - c->Copy = req->Copy; - write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy); - } - if (req->Present & PRESENT_OPTION) { - if (s->functions == 1) { - c->Option = req->ConfigIndex & COR_CONFIG_MASK; - } else { - c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK; - c->Option |= COR_FUNC_ENA|COR_IREQ_ENA; - if (req->Present & PRESENT_IOBASE_0) - c->Option |= COR_ADDR_DECODE; - } - if (c->state & CONFIG_IRQ_REQ) - if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) - c->Option |= COR_LEVEL_REQ; - write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); - mdelay(40); - } - if (req->Present & PRESENT_STATUS) { - c->Status = req->Status; - write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status); - } - if (req->Present & PRESENT_PIN_REPLACE) { - c->Pin = req->Pin; - write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin); - } - if (req->Present & PRESENT_EXT_STATUS) { - c->ExtStatus = req->ExtStatus; - write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus); - } - if (req->Present & PRESENT_IOBASE_0) { - u_char b = c->io.BasePort1 & 0xff; - write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b); - b = (c->io.BasePort1 >> 8) & 0xff; - write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b); - } - if (req->Present & PRESENT_IOSIZE) { - u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; - write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); - } - - /* Configure I/O windows */ - if (c->state & CONFIG_IO_REQ) { - iomap.speed = io_speed; - for (i = 0; i < MAX_IO_WIN; i++) - if (s->io[i].NumPorts != 0) { - iomap.map = i; - iomap.flags = MAP_ACTIVE; - switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) { - case IO_DATA_PATH_WIDTH_16: - iomap.flags |= MAP_16BIT; break; - case IO_DATA_PATH_WIDTH_AUTO: - iomap.flags |= MAP_AUTOSZ; break; - default: - break; - } - iomap.start = s->io[i].BasePort; - iomap.stop = iomap.start + s->io[i].NumPorts - 1; - s->ops->set_io_map(s, &iomap); - s->io[i].Config++; - } - } - - c->state |= CONFIG_LOCKED; - handle->state |= CLIENT_CONFIG_LOCKED; - return CS_SUCCESS; -} /* request_configuration */ - -/*====================================================================== - - Request_io() reserves ranges of port addresses for a socket. - I have not implemented range sharing or alias addressing. - -======================================================================*/ - -int pcmcia_request_io(client_handle_t handle, io_req_t *req) -{ - struct pcmcia_socket *s; - config_t *c; - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - - if (handle->state & CLIENT_CARDBUS) { -#ifdef CONFIG_CARDBUS - handle->state |= CLIENT_IO_REQ; - return CS_SUCCESS; -#else - return CS_UNSUPPORTED_FUNCTION; -#endif - } - - if (!req) - return CS_UNSUPPORTED_MODE; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->state & CONFIG_IO_REQ) - return CS_IN_USE; - if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) - return CS_BAD_ATTRIBUTE; - if ((req->NumPorts2 > 0) && - (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) - return CS_BAD_ATTRIBUTE; - - if (alloc_io_space(s, req->Attributes1, &req->BasePort1, - req->NumPorts1, req->IOAddrLines)) - return CS_IN_USE; - - if (req->NumPorts2) { - if (alloc_io_space(s, req->Attributes2, &req->BasePort2, - req->NumPorts2, req->IOAddrLines)) { - release_io_space(s, req->BasePort1, req->NumPorts1); - return CS_IN_USE; - } - } - - c->io = *req; - c->state |= CONFIG_IO_REQ; - handle->state |= CLIENT_IO_REQ; - return CS_SUCCESS; -} /* request_io */ - -/*====================================================================== - - Request_irq() reserves an irq for this client. - - Also, since Linux only reserves irq's when they are actually - hooked, we don't guarantee that an irq will still be available - when the configuration is locked. Now that I think about it, - there might be a way to fix this using a dummy handler. - -======================================================================*/ - -#ifdef CONFIG_PCMCIA_PROBE -static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs) -{ - return IRQ_NONE; -} -#endif - -int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) -{ - struct pcmcia_socket *s; - config_t *c; - int ret = CS_IN_USE, irq = 0; - struct pcmcia_device *p_dev = handle_to_pdev(handle); - - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - c = CONFIG(handle); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->state & CONFIG_IRQ_REQ) - return CS_IN_USE; - -#ifdef CONFIG_PCMCIA_PROBE - if (s->irq.AssignedIRQ != 0) { - /* If the interrupt is already assigned, it must be the same */ - irq = s->irq.AssignedIRQ; - } else { - int try; - u32 mask = s->irq_mask; - void *data = NULL; - - for (try = 0; try < 64; try++) { - irq = try % 32; - - /* marked as available by driver, and not blocked by userspace? */ - if (!((mask >> irq) & 1)) - continue; - - /* avoid an IRQ which is already used by a PCMCIA card */ - if ((try < 32) && pcmcia_used_irq[irq]) - continue; - - /* register the correct driver, if possible, of check whether - * registering a dummy handle works, i.e. if the IRQ isn't - * marked as used by the kernel resource management core */ - ret = request_irq(irq, - (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || - (s->functions > 1) || - (irq == s->pci_irq)) ? SA_SHIRQ : 0, - p_dev->dev.bus_id, - (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data); - if (!ret) { - if (!(req->Attributes & IRQ_HANDLE_PRESENT)) - free_irq(irq, data); - break; - } - } - } -#endif - if (ret) { - if (!s->pci_irq) - return ret; - irq = s->pci_irq; - } - - if (ret && req->Attributes & IRQ_HANDLE_PRESENT) { - if (request_irq(irq, req->Handler, - ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || - (s->functions > 1) || - (irq == s->pci_irq)) ? SA_SHIRQ : 0, - p_dev->dev.bus_id, req->Instance)) - return CS_IN_USE; - } - - c->irq.Attributes = req->Attributes; - s->irq.AssignedIRQ = req->AssignedIRQ = irq; - s->irq.Config++; - - c->state |= CONFIG_IRQ_REQ; - handle->state |= CLIENT_IRQ_REQ; - -#ifdef CONFIG_PCMCIA_PROBE - pcmcia_used_irq[irq]++; -#endif - - return CS_SUCCESS; -} /* pcmcia_request_irq */ - -/*====================================================================== - - Request_window() establishes a mapping between card memory space - and system memory space. - -======================================================================*/ - -int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) -{ - struct pcmcia_socket *s; - window_t *win; - u_long align; - int w; - - if (CHECK_HANDLE(*handle)) - return CS_BAD_HANDLE; - s = (*handle)->Socket; - if (!(s->state & SOCKET_PRESENT)) - return CS_NO_CARD; - if (req->Attributes & (WIN_PAGED | WIN_SHARED)) - return CS_BAD_ATTRIBUTE; - - /* Window size defaults to smallest available */ - if (req->Size == 0) - req->Size = s->map_size; - align = (((s->features & SS_CAP_MEM_ALIGN) || - (req->Attributes & WIN_STRICT_ALIGN)) ? - req->Size : s->map_size); - if (req->Size & (s->map_size-1)) - return CS_BAD_SIZE; - if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) || - (req->Base & (align-1))) - return CS_BAD_BASE; - if (req->Base) - align = 0; - - /* Allocate system memory window */ - for (w = 0; w < MAX_WIN; w++) - if (!(s->state & SOCKET_WIN_REQ(w))) break; - if (w == MAX_WIN) - return CS_OUT_OF_RESOURCE; - - win = &s->win[w]; - win->magic = WINDOW_MAGIC; - win->index = w; - win->handle = *handle; - win->sock = s; - - if (!(s->features & SS_CAP_STATIC_MAP)) { - win->ctl.res = find_mem_region(req->Base, req->Size, align, - (req->Attributes & WIN_MAP_BELOW_1MB), s); - if (!win->ctl.res) - return CS_IN_USE; - } - (*handle)->state |= CLIENT_WIN_REQ(w); - - /* Configure the socket controller */ - win->ctl.map = w+1; - win->ctl.flags = 0; - win->ctl.speed = req->AccessSpeed; - if (req->Attributes & WIN_MEMORY_TYPE) - win->ctl.flags |= MAP_ATTRIB; - if (req->Attributes & WIN_ENABLE) - win->ctl.flags |= MAP_ACTIVE; - if (req->Attributes & WIN_DATA_WIDTH_16) - win->ctl.flags |= MAP_16BIT; - if (req->Attributes & WIN_USE_WAIT) - win->ctl.flags |= MAP_USE_WAIT; - win->ctl.card_start = 0; - if (s->ops->set_mem_map(s, &win->ctl) != 0) - return CS_BAD_ARGS; - s->state |= SOCKET_WIN_REQ(w); - - /* Return window handle */ - if (s->features & SS_CAP_STATIC_MAP) { - req->Base = win->ctl.static_start; - } else { - req->Base = win->ctl.res->start; - } - *wh = win; - - return CS_SUCCESS; -} /* request_window */ - -/*====================================================================== - - I'm not sure which "reset" function this is supposed to use, - but for now, it uses the low-level interface's reset, not the - CIS register. - -======================================================================*/ +/* I'm not sure which "reset" function this is supposed to use, + * but for now, it uses the low-level interface's reset, not the + * CIS register. + */ int pccard_reset_card(struct pcmcia_socket *skt) { int ret; - + cs_dbg(skt, 1, "resetting socket\n"); down(&skt->skt_sem); @@ -1757,17 +778,14 @@ int pccard_reset_card(struct pcmcia_socket *skt) } /* reset_card */ EXPORT_SYMBOL(pccard_reset_card); -/*====================================================================== - - These shut down or wake up a socket. They are sort of user - initiated versions of the APM suspend and resume actions. - -======================================================================*/ +/* These shut down or wake up a socket. They are sort of user + * initiated versions of the APM suspend and resume actions. + */ int pcmcia_suspend_card(struct pcmcia_socket *skt) { int ret; - + cs_dbg(skt, 1, "suspending socket\n"); down(&skt->skt_sem); @@ -1786,6 +804,8 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt) return ret; } /* suspend_card */ +EXPORT_SYMBOL(pcmcia_suspend_card); + int pcmcia_resume_card(struct pcmcia_socket *skt) { @@ -1809,13 +829,10 @@ int pcmcia_resume_card(struct pcmcia_socket *skt) return ret; } /* resume_card */ +EXPORT_SYMBOL(pcmcia_resume_card); -/*====================================================================== - - These handle user requests to eject or insert a card. - -======================================================================*/ +/* These handle user requests to eject or insert a card. */ int pcmcia_eject_card(struct pcmcia_socket *skt) { int ret; @@ -1842,6 +859,8 @@ int pcmcia_eject_card(struct pcmcia_socket *skt) return ret; } /* eject_card */ +EXPORT_SYMBOL(pcmcia_eject_card); + int pcmcia_insert_card(struct pcmcia_socket *skt) { @@ -1865,37 +884,38 @@ int pcmcia_insert_card(struct pcmcia_socket *skt) return ret; } /* insert_card */ +EXPORT_SYMBOL(pcmcia_insert_card); -/*====================================================================== - OS-specific module glue goes here - -======================================================================*/ -/* in alpha order */ -EXPORT_SYMBOL(pcmcia_eject_card); -EXPORT_SYMBOL(pcmcia_get_card_services_info); -EXPORT_SYMBOL(pcmcia_get_mem_page); -EXPORT_SYMBOL(pcmcia_insert_card); -EXPORT_SYMBOL(pcmcia_map_mem_page); -EXPORT_SYMBOL(pcmcia_modify_configuration); -EXPORT_SYMBOL(pcmcia_release_configuration); -EXPORT_SYMBOL(pcmcia_release_io); -EXPORT_SYMBOL(pcmcia_release_irq); -EXPORT_SYMBOL(pcmcia_release_window); -EXPORT_SYMBOL(pcmcia_replace_cis); -EXPORT_SYMBOL(pcmcia_request_configuration); -EXPORT_SYMBOL(pcmcia_request_io); -EXPORT_SYMBOL(pcmcia_request_irq); -EXPORT_SYMBOL(pcmcia_request_window); -EXPORT_SYMBOL(pcmcia_resume_card); -EXPORT_SYMBOL(pcmcia_suspend_card); +static int pcmcia_socket_hotplug(struct class_device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev); + int i = 0, length = 0; + + if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, + &length, "SOCKET_NO=%u", s->sock)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + + +static struct completion pcmcia_unload; + +static void pcmcia_release_socket_class(struct class *data) +{ + complete(&pcmcia_unload); +} -EXPORT_SYMBOL(dead_socket); -EXPORT_SYMBOL(pcmcia_parse_events); struct class pcmcia_socket_class = { .name = "pcmcia_socket", + .hotplug = pcmcia_socket_hotplug, .release = pcmcia_release_socket, + .class_release = pcmcia_release_socket_class, }; EXPORT_SYMBOL(pcmcia_socket_class); @@ -1903,9 +923,8 @@ EXPORT_SYMBOL(pcmcia_socket_class); static int __init init_pcmcia_cs(void) { int ret; - printk(KERN_INFO "%s\n", release); - printk(KERN_INFO " %s\n", options); + init_completion(&pcmcia_unload); ret = class_register(&pcmcia_socket_class); if (ret) return (ret); @@ -1914,13 +933,12 @@ static int __init init_pcmcia_cs(void) static void __exit exit_pcmcia_cs(void) { - printk(KERN_INFO "unloading Kernel Card Services\n"); - class_interface_unregister(&pccard_sysfs_interface); - class_unregister(&pcmcia_socket_class); + class_interface_unregister(&pccard_sysfs_interface); + class_unregister(&pcmcia_socket_class); + + wait_for_completion(&pcmcia_unload); } subsys_initcall(init_pcmcia_cs); module_exit(exit_pcmcia_cs); -/*====================================================================*/ - diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 7933a7db49d..0b4c18edfa4 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -123,9 +123,9 @@ void cb_free(struct pcmcia_socket *s); int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, void *ptr); /* In cistpl.c */ -int read_cis_mem(struct pcmcia_socket *s, int attr, +int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); -void write_cis_mem(struct pcmcia_socket *s, int attr, +void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); void release_cis_mem(struct pcmcia_socket *s); void destroy_cis_cache(struct pcmcia_socket *s); @@ -134,13 +134,12 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function, cisdata_t /* In rsrc_mgr */ void pcmcia_validate_mem(struct pcmcia_socket *s); -struct resource *find_io_region(unsigned long base, int num, unsigned long align, +struct resource *pcmcia_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s); -int adjust_io_region(struct resource *res, unsigned long r_start, +int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, unsigned long r_end, struct pcmcia_socket *s); -struct resource *find_mem_region(u_long base, u_long num, u_long align, +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s); -int adjust_resource_info(client_handle_t handle, adjust_t *adj); void release_resource_db(struct pcmcia_socket *s); /* In socket_sysfs.c */ @@ -159,7 +158,7 @@ int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int f struct pcmcia_callback{ struct module *owner; int (*event) (struct pcmcia_socket *s, event_t event, int priority); - int (*resources_done) (struct pcmcia_socket *s); + void (*requery) (struct pcmcia_socket *s); }; int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c); diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 569e55feecf..cabddd49f6f 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -10,44 +10,29 @@ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds - * (C) 2003 - 2004 Dominik Brodowski + * (C) 2003 - 2005 Dominik Brodowski */ #include <linux/config.h> +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/init.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/string.h> #include <linux/errno.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/fcntl.h> -#include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/timer.h> -#include <linux/ioctl.h> -#include <linux/proc_fs.h> -#include <linux/poll.h> -#include <linux/pci.h> #include <linux/list.h> #include <linux/delay.h> -#include <linux/kref.h> #include <linux/workqueue.h> - -#include <asm/atomic.h> +#include <linux/crc32.h> +#include <linux/firmware.h> #define IN_CARD_SERVICES -#include <pcmcia/version.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> -#include <pcmcia/bulkmem.h> #include <pcmcia/cistpl.h> #include <pcmcia/ds.h> #include <pcmcia/ss.h> #include "cs_internal.h" +#include "ds_internal.h" /*====================================================================*/ @@ -70,49 +55,9 @@ module_param_named(pc_debug, ds_pc_debug, int, 0644); #define ds_dbg(lvl, fmt, arg...) do { } while (0) #endif -/*====================================================================*/ +spinlock_t pcmcia_dev_list_lock; -/* Device user information */ -#define MAX_EVENTS 32 -#define USER_MAGIC 0x7ea4 -#define CHECK_USER(u) \ - (((u) == NULL) || ((u)->user_magic != USER_MAGIC)) -typedef struct user_info_t { - u_int user_magic; - int event_head, event_tail; - event_t event[MAX_EVENTS]; - struct user_info_t *next; - struct pcmcia_bus_socket *socket; -} user_info_t; - -/* Socket state information */ -struct pcmcia_bus_socket { - struct kref refcount; - struct pcmcia_callback callback; - int state; - user_info_t *user; - wait_queue_head_t queue; - struct pcmcia_socket *parent; - - /* the PCMCIA devices connected to this socket (normally one, more - * for multifunction devices: */ - struct list_head devices_list; - u8 device_count; /* the number of devices, used - * only internally and subject - * to incorrectness and change */ -}; -static spinlock_t pcmcia_dev_list_lock; - -#define DS_SOCKET_PRESENT 0x01 -#define DS_SOCKET_BUSY 0x02 -#define DS_SOCKET_REMOVAL_PENDING 0x10 -#define DS_SOCKET_DEAD 0x80 - -/*====================================================================*/ - -static int major_dev = -1; - -static int unbind_request(struct pcmcia_bus_socket *s); +static int unbind_request(struct pcmcia_socket *s); /*====================================================================*/ @@ -213,7 +158,7 @@ static const lookup_t service_table[] = { }; -int pcmcia_report_error(client_handle_t handle, error_info_t *err) +static int pcmcia_report_error(client_handle_t handle, error_info_t *err) { int i; char *serv; @@ -243,7 +188,6 @@ int pcmcia_report_error(client_handle_t handle, error_info_t *err) return CS_SUCCESS; } /* report_error */ -EXPORT_SYMBOL(pcmcia_report_error); /* end of code which was in cs.c before */ @@ -256,29 +200,101 @@ void cs_error(client_handle_t handle, int func, int ret) } EXPORT_SYMBOL(cs_error); -/*======================================================================*/ - -static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info); -static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr); -static void pcmcia_release_bus_socket(struct kref *refcount) +static void pcmcia_check_driver(struct pcmcia_driver *p_drv) { - struct pcmcia_bus_socket *s = container_of(refcount, struct pcmcia_bus_socket, refcount); - pcmcia_put_socket(s->parent); - kfree(s); + struct pcmcia_device_id *did = p_drv->id_table; + unsigned int i; + u32 hash; + + while (did && did->match_flags) { + for (i=0; i<4; i++) { + if (!did->prod_id[i]) + continue; + + hash = crc32(0, did->prod_id[i], strlen(did->prod_id[i])); + if (hash == did->prod_id_hash[i]) + continue; + + printk(KERN_DEBUG "pcmcia: %s: invalid hash for " + "product string \"%s\": is 0x%x, should " + "be 0x%x\n", p_drv->drv.name, did->prod_id[i], + did->prod_id_hash[i], hash); + printk(KERN_DEBUG "pcmcia: see " + "Documentation/pcmcia/devicetable.txt for " + "details\n"); + } + did++; + } + + return; } -static void pcmcia_put_bus_socket(struct pcmcia_bus_socket *s) + +#ifdef CONFIG_PCMCIA_LOAD_CIS + +/** + * pcmcia_load_firmware - load CIS from userspace if device-provided is broken + * @dev - the pcmcia device which needs a CIS override + * @filename - requested filename in /lib/firmware/cis/ + * + * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if + * the one provided by the card is broken. The firmware files reside in + * /lib/firmware/cis/ in userspace. + */ +static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { - kref_put(&s->refcount, pcmcia_release_bus_socket); + struct pcmcia_socket *s = dev->socket; + const struct firmware *fw; + char path[20]; + int ret=-ENOMEM; + cisdump_t *cis; + + if (!filename) + return -EINVAL; + + ds_dbg(1, "trying to load firmware %s\n", filename); + + if (strlen(filename) > 14) + return -EINVAL; + + snprintf(path, 20, "%s", filename); + + if (request_firmware(&fw, path, &dev->dev) == 0) { + if (fw->size >= CISTPL_MAX_CIS_SIZE) + goto release; + + cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL); + if (!cis) + goto release; + + memset(cis, 0, sizeof(cisdump_t)); + + cis->Length = fw->size + 1; + memcpy(cis->Data, fw->data, fw->size); + + if (!pcmcia_replace_cis(s, cis)) + ret = 0; + } + release: + release_firmware(fw); + + return (ret); } -static struct pcmcia_bus_socket *pcmcia_get_bus_socket(struct pcmcia_bus_socket *s) +#else /* !CONFIG_PCMCIA_LOAD_CIS */ + +static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { - kref_get(&s->refcount); - return (s); + return -ENODEV; } +#endif + + +/*======================================================================*/ + + /** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @@ -292,6 +308,8 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) if (!driver) return -EINVAL; + pcmcia_check_driver(driver); + /* initialize common fields */ driver->drv.bus = &pcmcia_bus_type; driver->drv.owner = driver->owner; @@ -311,42 +329,10 @@ void pcmcia_unregister_driver(struct pcmcia_driver *driver) } EXPORT_SYMBOL(pcmcia_unregister_driver); -#ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_pccard = NULL; - -static int proc_read_drivers_callback(struct device_driver *driver, void *d) -{ - char **p = d; - struct pcmcia_driver *p_drv = container_of(driver, - struct pcmcia_driver, drv); - - *p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name, -#ifdef CONFIG_MODULE_UNLOAD - (p_drv->owner) ? module_refcount(p_drv->owner) : 1 -#else - 1 -#endif - ); - d = (void *) p; - - return 0; -} - -static int proc_read_drivers(char *buf, char **start, off_t pos, - int count, int *eof, void *data) -{ - char *p = buf; - - bus_for_each_drv(&pcmcia_bus_type, NULL, - (void *) &p, proc_read_drivers_callback); - - return (p - buf); -} -#endif /* pcmcia_device handling */ -static struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) +struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) { struct device *tmp_dev; tmp_dev = get_device(&p_dev->dev); @@ -355,7 +341,7 @@ static struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev) return to_pcmcia_dev(tmp_dev); } -static void pcmcia_put_dev(struct pcmcia_device *p_dev) +void pcmcia_put_dev(struct pcmcia_device *p_dev) { if (p_dev) put_device(&p_dev->dev); @@ -365,7 +351,7 @@ static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); ds_dbg(1, "releasing dev %p\n", p_dev); - pcmcia_put_bus_socket(p_dev->socket->pcmcia); + pcmcia_put_socket(p_dev->socket); kfree(p_dev); } @@ -500,34 +486,38 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) */ static DECLARE_MUTEX(device_add_lock); -static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, unsigned int function) +struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { struct pcmcia_device *p_dev; unsigned long flags; - s = pcmcia_get_bus_socket(s); + s = pcmcia_get_socket(s); if (!s) return NULL; down(&device_add_lock); + /* max of 2 devices per card */ + if (s->device_count == 2) + goto err_put; + p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); if (!p_dev) goto err_put; memset(p_dev, 0, sizeof(struct pcmcia_device)); - p_dev->socket = s->parent; + p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; p_dev->dev.bus = &pcmcia_bus_type; - p_dev->dev.parent = s->parent->dev.dev; + p_dev->dev.parent = s->dev.dev; p_dev->dev.release = pcmcia_release_dev; sprintf (p_dev->dev.bus_id, "%d.%d", p_dev->socket->sock, p_dev->device_no); /* compat */ p_dev->client.client_magic = CLIENT_MAGIC; - p_dev->client.Socket = s->parent; + p_dev->client.Socket = s; p_dev->client.Function = function; p_dev->client.state = CLIENT_UNBOUND; @@ -536,6 +526,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns list_add_tail(&p_dev->socket_device_list, &s->devices_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + pcmcia_device_query(p_dev); + if (device_register(&p_dev->dev)) { spin_lock_irqsave(&pcmcia_dev_list_lock, flags); list_del(&p_dev->socket_device_list); @@ -553,7 +545,7 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns s->device_count--; err_put: up(&device_add_lock); - pcmcia_put_bus_socket(s); + pcmcia_put_socket(s); return NULL; } @@ -584,23 +576,252 @@ static int pcmcia_card_add(struct pcmcia_socket *s) /* this doesn't handle multifunction devices on one pcmcia function * yet. */ for (i=0; i < no_funcs; i++) - pcmcia_device_add(s->pcmcia, i); + pcmcia_device_add(s, i); return (ret); } +static void pcmcia_delayed_add_pseudo_device(void *data) +{ + struct pcmcia_socket *s = data; + pcmcia_device_add(s, 0); + s->pcmcia_state.device_add_pending = 0; +} + +static inline void pcmcia_add_pseudo_device(struct pcmcia_socket *s) +{ + if (!s->pcmcia_state.device_add_pending) { + schedule_work(&s->device_add); + s->pcmcia_state.device_add_pending = 1; + } + return; +} + +static int pcmcia_requery(struct device *dev, void * _data) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + if (!p_dev->dev.driver) + pcmcia_device_query(p_dev); + + return 0; +} + +static void pcmcia_bus_rescan(struct pcmcia_socket *skt) +{ + int no_devices=0; + unsigned long flags; + + /* must be called with skt_sem held */ + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + if (list_empty(&skt->devices_list)) + no_devices=1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + /* if no devices were added for this socket yet because of + * missing resource information or other trouble, we need to + * do this now. */ + if (no_devices) { + int ret = pcmcia_card_add(skt); + if (ret) + return; + } + + /* some device information might have changed because of a CIS + * update or because we can finally read it correctly... so + * determine it again, overwriting old values if necessary. */ + bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery); + + /* we re-scan all devices, not just the ones connected to this + * socket. This does not matter, though. */ + bus_rescan_devices(&pcmcia_bus_type); +} + +static inline int pcmcia_devmatch(struct pcmcia_device *dev, + struct pcmcia_device_id *did) +{ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) { + if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) { + if ((!dev->has_card_id) || (dev->card_id != did->card_id)) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) { + if (dev->func != did->function) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) { + if (!dev->prod_id[0]) + return 0; + if (strcmp(did->prod_id[0], dev->prod_id[0])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) { + if (!dev->prod_id[1]) + return 0; + if (strcmp(did->prod_id[1], dev->prod_id[1])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) { + if (!dev->prod_id[2]) + return 0; + if (strcmp(did->prod_id[2], dev->prod_id[2])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) { + if (!dev->prod_id[3]) + return 0; + if (strcmp(did->prod_id[3], dev->prod_id[3])) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { + /* handle pseudo multifunction devices: + * there are at most two pseudo multifunction devices. + * if we're matching against the first, schedule a + * call which will then check whether there are two + * pseudo devices, and if not, add the second one. + */ + if (dev->device_no == 0) + pcmcia_add_pseudo_device(dev->socket); + + if (dev->device_no != did->device_no) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { + if ((!dev->has_func_id) || (dev->func_id != did->func_id)) + return 0; + + /* if this is a pseudo-multi-function device, + * we need explicit matches */ + if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) + return 0; + if (dev->device_no) + return 0; + + /* also, FUNC_ID matching needs to be activated by userspace + * after it has re-checked that there is no possible module + * with a prod_id/manf_id/card_id match. + */ + if (!dev->allow_func_id_match) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) { + if (!dev->socket->fake_cis) + pcmcia_load_firmware(dev, did->cisfile); + + if (!dev->socket->fake_cis) + return 0; + } + + if (did->match_flags & PCMCIA_DEV_ID_MATCH_ANONYMOUS) { + int i; + for (i=0; i<4; i++) + if (dev->prod_id[i]) + return 0; + if (dev->has_manf_id || dev->has_card_id || dev->has_func_id) + return 0; + } + + dev->dev.driver_data = (void *) did; + + return 1; +} + + static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_device * p_dev = to_pcmcia_dev(dev); struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); + struct pcmcia_device_id *did = p_drv->id_table; /* matching by cardmgr */ if (p_dev->cardmgr == p_drv) return 1; + while (did && did->match_flags) { + if (pcmcia_devmatch(p_dev, did)) + return 1; + did++; + } + return 0; } +#ifdef CONFIG_HOTPLUG + +static int pcmcia_bus_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct pcmcia_device *p_dev; + int i, length = 0; + u32 hash[4] = { 0, 0, 0, 0}; + + if (!dev) + return -ENODEV; + + p_dev = to_pcmcia_dev(dev); + + /* calculate hashes */ + for (i=0; i<4; i++) { + if (!p_dev->prod_id[i]) + continue; + hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i])); + } + + i = 0; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "SOCKET_NO=%u", + p_dev->socket->sock)) + return -ENOMEM; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVICE_NO=%02X", + p_dev->device_no)) + return -ENOMEM; + + if (add_hotplug_env_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X" + "pa%08Xpb%08Xpc%08Xpd%08X", + p_dev->has_manf_id ? p_dev->manf_id : 0, + p_dev->has_card_id ? p_dev->card_id : 0, + p_dev->has_func_id ? p_dev->func_id : 0, + p_dev->func, + p_dev->device_no, + hash[0], + hash[1], + hash[2], + hash[3])) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +#else + +static int pcmcia_bus_hotplug(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + return -ENODEV; +} + +#endif + /************************ per-device sysfs output ***************************/ #define pcmcia_device_attr(field, test, format) \ @@ -626,6 +847,43 @@ pcmcia_device_stringattr(prod_id2, prod_id[1]); pcmcia_device_stringattr(prod_id3, prod_id[2]); pcmcia_device_stringattr(prod_id4, prod_id[3]); +static ssize_t modalias_show(struct device *dev, char *buf) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + int i; + u32 hash[4] = { 0, 0, 0, 0}; + + /* calculate hashes */ + for (i=0; i<4; i++) { + if (!p_dev->prod_id[i]) + continue; + hash[i] = crc32(0,p_dev->prod_id[i],strlen(p_dev->prod_id[i])); + } + return sprintf(buf, "pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X" + "pa%08Xpb%08Xpc%08Xpd%08X\n", + p_dev->has_manf_id ? p_dev->manf_id : 0, + p_dev->has_card_id ? p_dev->card_id : 0, + p_dev->has_func_id ? p_dev->func_id : 0, + p_dev->func, p_dev->device_no, + hash[0], hash[1], hash[2], hash[3]); +} + +static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct pcmcia_device *p_dev = to_pcmcia_dev(dev); + if (!count) + return -EINVAL; + + down(&p_dev->socket->skt_sem); + p_dev->allow_func_id_match = 1; + up(&p_dev->socket->skt_sem); + + bus_rescan_devices(&pcmcia_bus_type); + + return count; +} + static struct device_attribute pcmcia_dev_attrs[] = { __ATTR(function, 0444, func_show, NULL), __ATTR_RO(func_id), @@ -635,46 +893,14 @@ static struct device_attribute pcmcia_dev_attrs[] = { __ATTR_RO(prod_id2), __ATTR_RO(prod_id3), __ATTR_RO(prod_id4), + __ATTR_RO(modalias), + __ATTR(allow_func_id_match, 0200, NULL, pcmcia_store_allow_func_id_match), __ATTR_NULL, }; /*====================================================================== - These manage a ring buffer of events pending for one user process - -======================================================================*/ - -static int queue_empty(user_info_t *user) -{ - return (user->event_head == user->event_tail); -} - -static event_t get_queued_event(user_info_t *user) -{ - user->event_tail = (user->event_tail+1) % MAX_EVENTS; - return user->event[user->event_tail]; -} - -static void queue_event(user_info_t *user, event_t event) -{ - user->event_head = (user->event_head+1) % MAX_EVENTS; - if (user->event_head == user->event_tail) - user->event_tail = (user->event_tail+1) % MAX_EVENTS; - user->event[user->event_head] = event; -} - -static void handle_event(struct pcmcia_bus_socket *s, event_t event) -{ - user_info_t *user; - for (user = s->user; user; user = user->next) - queue_event(user, event); - wake_up_interruptible(&s->queue); -} - - -/*====================================================================== - The card status event handler. ======================================================================*/ @@ -706,21 +932,13 @@ static int send_event_callback(struct device *dev, void * _data) static int send_event(struct pcmcia_socket *s, event_t event, int priority) { - int ret = 0; struct send_event_data private; - struct pcmcia_bus_socket *skt = pcmcia_get_bus_socket(s->pcmcia); - - if (!skt) - return 0; private.skt = s; private.event = event; private.priority = priority; - ret = bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); - - pcmcia_put_bus_socket(skt); - return ret; + return bus_for_each_dev(&pcmcia_bus_type, NULL, &private, send_event_callback); } /* send_event */ @@ -731,25 +949,25 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority) static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) { - struct pcmcia_bus_socket *s = skt->pcmcia; + struct pcmcia_socket *s = pcmcia_get_socket(skt); int ret = 0; ds_dbg(1, "ds_event(0x%06x, %d, 0x%p)\n", - event, priority, s); + event, priority, skt); switch (event) { case CS_EVENT_CARD_REMOVAL: - s->state &= ~DS_SOCKET_PRESENT; + s->pcmcia_state.present = 0; send_event(skt, event, priority); - unbind_request(s); - handle_event(s, event); + unbind_request(skt); + handle_event(skt, event); break; case CS_EVENT_CARD_INSERTION: - s->state |= DS_SOCKET_PRESENT; + s->pcmcia_state.present = 1; pcmcia_card_add(skt); - handle_event(s, event); + handle_event(skt, event); break; case CS_EVENT_EJECTION_REQUEST: @@ -757,137 +975,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) break; default: - handle_event(s, event); + handle_event(skt, event); send_event(skt, event, priority); break; } + pcmcia_put_socket(s); + return 0; } /* ds_event */ -/*====================================================================== - - bind_request() and bind_device() are merged by now. Register_client() - is called right at the end of bind_request(), during the driver's - ->attach() call. Individual descriptions: - - bind_request() connects a socket to a particular client driver. - It looks up the specified device ID in the list of registered - drivers, binds it to the socket, and tries to create an instance - of the device. unbind_request() deletes a driver instance. - - Bind_device() associates a device driver with a particular socket. - It is normally called by Driver Services after it has identified - a newly inserted card. An instance of that driver will then be - eligible to register as a client of this socket. - - Register_client() uses the dev_info_t handle to match the - caller with a socket. The driver must have already been bound - to a socket with bind_device() -- in fact, bind_device() - allocates the client structure that will be used. - -======================================================================*/ - -static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) -{ - struct pcmcia_driver *p_drv; - struct pcmcia_device *p_dev; - int ret = 0; - unsigned long flags; - - s = pcmcia_get_bus_socket(s); - if (!s) - return -EINVAL; - - ds_dbg(2, "bind_request(%d, '%s')\n", s->parent->sock, - (char *)bind_info->dev_info); - - p_drv = get_pcmcia_driver(&bind_info->dev_info); - if (!p_drv) { - ret = -EINVAL; - goto err_put; - } - - if (!try_module_get(p_drv->owner)) { - ret = -EINVAL; - goto err_put_driver; - } - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { - if (p_dev->func == bind_info->function) { - if ((p_dev->dev.driver == &p_drv->drv)) { - if (p_dev->cardmgr) { - /* if there's already a device - * registered, and it was registered - * by userspace before, we need to - * return the "instance". */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - bind_info->instance = p_dev->instance; - ret = -EBUSY; - goto err_put_module; - } else { - /* the correct driver managed to bind - * itself magically to the correct - * device. */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - p_dev->cardmgr = p_drv; - ret = 0; - goto err_put_module; - } - } else if (!p_dev->dev.driver) { - /* there's already a device available where - * no device has been bound to yet. So we don't - * need to register a device! */ - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - goto rescan; - } - } - } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - - p_dev = pcmcia_device_add(s, bind_info->function); - if (!p_dev) { - ret = -EIO; - goto err_put_module; - } - -rescan: - p_dev->cardmgr = p_drv; - - pcmcia_device_query(p_dev); - - /* - * Prevent this racing with a card insertion. - */ - down(&s->parent->skt_sem); - bus_rescan_devices(&pcmcia_bus_type); - up(&s->parent->skt_sem); - - /* check whether the driver indeed matched. I don't care if this - * is racy or not, because it can only happen on cardmgr access - * paths... - */ - if (!(p_dev->dev.driver == &p_drv->drv)) - p_dev->cardmgr = NULL; - - err_put_module: - module_put(p_drv->owner); - err_put_driver: - put_driver(&p_drv->drv); - err_put: - pcmcia_put_bus_socket(s); - - return (ret); -} /* bind_request */ - int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) { client_t *client = NULL; - struct pcmcia_socket *s; - struct pcmcia_bus_socket *skt = NULL; + struct pcmcia_socket *s = NULL; struct pcmcia_device *p_dev = NULL; /* Look for unbound client with matching dev_info */ @@ -898,14 +1001,11 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) if (s->state & SOCKET_CARDBUS) continue; - skt = s->pcmcia; - if (!skt) - continue; - skt = pcmcia_get_bus_socket(skt); - if (!skt) + s = pcmcia_get_socket(s); + if (!s) continue; spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &skt->devices_list, socket_device_list) { + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { struct pcmcia_driver *p_drv; p_dev = pcmcia_get_dev(p_dev); if (!p_dev) @@ -924,14 +1024,14 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) pcmcia_put_dev(p_dev); } spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - pcmcia_put_bus_socket(skt); + pcmcia_put_socket(s); } found: up_read(&pcmcia_socket_list_rwsem); if (!p_dev || !client) return -ENODEV; - pcmcia_put_bus_socket(skt); /* safe, as we already hold a reference from bind_device */ + pcmcia_put_socket(s); /* safe, as we already hold a reference from bind_device */ *handle = client; client->state &= ~CLIENT_UNBOUND; @@ -978,106 +1078,15 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) EXPORT_SYMBOL(pcmcia_register_client); -/*====================================================================*/ - -extern struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s); - -static int get_device_info(struct pcmcia_bus_socket *s, bind_info_t *bind_info, int first) -{ - dev_node_t *node; - struct pcmcia_device *p_dev; - unsigned long flags; - int ret = 0; - -#ifdef CONFIG_CARDBUS - /* - * Some unbelievably ugly code to associate the PCI cardbus - * device and its driver with the PCMCIA "bind" information. - */ - { - struct pci_bus *bus; - - bus = pcmcia_lookup_bus(s->parent); - if (bus) { - struct list_head *list; - struct pci_dev *dev = NULL; - - list = bus->devices.next; - while (list != &bus->devices) { - struct pci_dev *pdev = pci_dev_b(list); - list = list->next; - - if (first) { - dev = pdev; - break; - } - - /* Try to handle "next" here some way? */ - } - if (dev && dev->driver) { - strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN); - bind_info->major = 0; - bind_info->minor = 0; - bind_info->next = NULL; - return 0; - } - } - } -#endif - - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { - if (p_dev->func == bind_info->function) { - p_dev = pcmcia_get_dev(p_dev); - if (!p_dev) - continue; - goto found; - } - } - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - return -ENODEV; - - found: - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - - if ((!p_dev->instance) || - (p_dev->instance->state & DEV_CONFIG_PENDING)) { - ret = -EAGAIN; - goto err_put; - } - - if (first) - node = p_dev->instance->dev; - else - for (node = p_dev->instance->dev; node; node = node->next) - if (node == bind_info->next) - break; - if (!node) { - ret = -ENODEV; - goto err_put; - } - - strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); - bind_info->major = node->major; - bind_info->minor = node->minor; - bind_info->next = node->next; - - err_put: - pcmcia_put_dev(p_dev); - return (ret); -} /* get_device_info */ - -/*====================================================================*/ - /* unbind _all_ devices attached to a given pcmcia_bus_socket. The * drivers have been called with EVENT_CARD_REMOVAL before. */ -static int unbind_request(struct pcmcia_bus_socket *s) +static int unbind_request(struct pcmcia_socket *s) { struct pcmcia_device *p_dev; unsigned long flags; - ds_dbg(2, "unbind_request(%d)\n", s->parent->sock); + ds_dbg(2, "unbind_request(%d)\n", s->sock); s->device_count = 0; @@ -1133,433 +1142,58 @@ int pcmcia_deregister_client(client_handle_t handle) } /* deregister_client */ EXPORT_SYMBOL(pcmcia_deregister_client); - -/*====================================================================== - - The user-mode PC Card device interface - -======================================================================*/ - -static int ds_open(struct inode *inode, struct file *file) -{ - socket_t i = iminor(inode); - struct pcmcia_bus_socket *s; - user_info_t *user; - - ds_dbg(0, "ds_open(socket %d)\n", i); - - s = get_socket_info_by_nr(i); - if (!s) - return -ENODEV; - s = pcmcia_get_bus_socket(s); - if (!s) - return -ENODEV; - - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - if (s->state & DS_SOCKET_BUSY) { - pcmcia_put_bus_socket(s); - return -EBUSY; - } - else - s->state |= DS_SOCKET_BUSY; - } - - user = kmalloc(sizeof(user_info_t), GFP_KERNEL); - if (!user) { - pcmcia_put_bus_socket(s); - return -ENOMEM; - } - user->event_tail = user->event_head = 0; - user->next = s->user; - user->user_magic = USER_MAGIC; - user->socket = s; - s->user = user; - file->private_data = user; - - if (s->state & DS_SOCKET_PRESENT) - queue_event(user, CS_EVENT_CARD_INSERTION); - return 0; -} /* ds_open */ - -/*====================================================================*/ - -static int ds_release(struct inode *inode, struct file *file) -{ - struct pcmcia_bus_socket *s; - user_info_t *user, **link; - - ds_dbg(0, "ds_release(socket %d)\n", iminor(inode)); - - user = file->private_data; - if (CHECK_USER(user)) - goto out; - - s = user->socket; - - /* Unlink user data structure */ - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - s->state &= ~DS_SOCKET_BUSY; - } - file->private_data = NULL; - for (link = &s->user; *link; link = &(*link)->next) - if (*link == user) break; - if (link == NULL) - goto out; - *link = user->next; - user->user_magic = 0; - kfree(user); - pcmcia_put_bus_socket(s); -out: - return 0; -} /* ds_release */ - -/*====================================================================*/ - -static ssize_t ds_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct pcmcia_bus_socket *s; - user_info_t *user; - int ret; - - ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); - - if (count < 4) - return -EINVAL; - - user = file->private_data; - if (CHECK_USER(user)) - return -EIO; - - s = user->socket; - if (s->state & DS_SOCKET_DEAD) - return -EIO; - - ret = wait_event_interruptible(s->queue, !queue_empty(user)); - if (ret == 0) - ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4; - - return ret; -} /* ds_read */ - -/*====================================================================*/ - -static ssize_t ds_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode)); - - if (count != 4) - return -EINVAL; - if ((file->f_flags & O_ACCMODE) == O_RDONLY) - return -EBADF; - - return -EIO; -} /* ds_write */ - -/*====================================================================*/ - -/* No kernel lock - fine */ -static u_int ds_poll(struct file *file, poll_table *wait) -{ - struct pcmcia_bus_socket *s; - user_info_t *user; - - ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode)); - - user = file->private_data; - if (CHECK_USER(user)) - return POLLERR; - s = user->socket; - /* - * We don't check for a dead socket here since that - * will send cardmgr into an endless spin. - */ - poll_wait(file, &s->queue, wait); - if (!queue_empty(user)) - return POLLIN | POLLRDNORM; - return 0; -} /* ds_poll */ - -/*====================================================================*/ - -extern int pcmcia_adjust_resource_info(adjust_t *adj); - -static int ds_ioctl(struct inode * inode, struct file * file, - u_int cmd, u_long arg) -{ - struct pcmcia_bus_socket *s; - void __user *uarg = (char __user *)arg; - u_int size; - int ret, err; - ds_ioctl_arg_t *buf; - user_info_t *user; - - ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); - - user = file->private_data; - if (CHECK_USER(user)) - return -EIO; - - s = user->socket; - if (s->state & DS_SOCKET_DEAD) - return -EIO; - - size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; - if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; - - /* Permission check */ - if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (cmd & IOC_IN) { - if (!access_ok(VERIFY_READ, uarg, size)) { - ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT); - return -EFAULT; - } - } - if (cmd & IOC_OUT) { - if (!access_ok(VERIFY_WRITE, uarg, size)) { - ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT); - return -EFAULT; - } - } - buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL); - if (!buf) - return -ENOMEM; - - err = ret = 0; - - if (cmd & IOC_IN) __copy_from_user((char *)buf, uarg, size); - - switch (cmd) { - case DS_ADJUST_RESOURCE_INFO: - ret = pcmcia_adjust_resource_info(&buf->adjust); - break; - case DS_GET_CARD_SERVICES_INFO: - ret = pcmcia_get_card_services_info(&buf->servinfo); - break; - case DS_GET_CONFIGURATION_INFO: - if (buf->config.Function && - (buf->config.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_get_configuration_info(s->parent, - buf->config.Function, &buf->config); - break; - case DS_GET_FIRST_TUPLE: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_get_first_tuple(s->parent, BIND_FN_ALL, &buf->tuple); - break; - case DS_GET_NEXT_TUPLE: - ret = pccard_get_next_tuple(s->parent, BIND_FN_ALL, &buf->tuple); - break; - case DS_GET_TUPLE_DATA: - buf->tuple.TupleData = buf->tuple_parse.data; - buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data); - ret = pccard_get_tuple_data(s->parent, &buf->tuple); - break; - case DS_PARSE_TUPLE: - buf->tuple.TupleData = buf->tuple_parse.data; - ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse); - break; - case DS_RESET_CARD: - ret = pccard_reset_card(s->parent); - break; - case DS_GET_STATUS: - if (buf->status.Function && - (buf->status.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_get_status(s->parent, buf->status.Function, &buf->status); - break; - case DS_VALIDATE_CIS: - down(&s->parent->skt_sem); - pcmcia_validate_mem(s->parent); - up(&s->parent->skt_sem); - ret = pccard_validate_cis(s->parent, BIND_FN_ALL, &buf->cisinfo); - break; - case DS_SUSPEND_CARD: - ret = pcmcia_suspend_card(s->parent); - break; - case DS_RESUME_CARD: - ret = pcmcia_resume_card(s->parent); - break; - case DS_EJECT_CARD: - err = pcmcia_eject_card(s->parent); - break; - case DS_INSERT_CARD: - err = pcmcia_insert_card(s->parent); - break; - case DS_ACCESS_CONFIGURATION_REGISTER: - if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { - err = -EPERM; - goto free_out; - } - if (buf->conf_reg.Function && - (buf->conf_reg.Function >= s->parent->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_access_configuration_register(s->parent, - buf->conf_reg.Function, &buf->conf_reg); - break; - case DS_GET_FIRST_REGION: - case DS_GET_NEXT_REGION: - case DS_BIND_MTD: - if (!capable(CAP_SYS_ADMIN)) { - err = -EPERM; - goto free_out; - } else { - static int printed = 0; - if (!printed) { - printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n"); - printk(KERN_WARNING "MTD handling any more.\n"); - printed++; - } - } - err = -EINVAL; - goto free_out; - break; - case DS_GET_FIRST_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, 0, - &buf->win_info.window); - break; - case DS_GET_NEXT_WINDOW: - ret = pcmcia_get_window(s->parent, &buf->win_info.handle, - buf->win_info.handle->index + 1, &buf->win_info.window); - break; - case DS_GET_MEM_PAGE: - ret = pcmcia_get_mem_page(buf->win_info.handle, - &buf->win_info.map); - break; - case DS_REPLACE_CIS: - ret = pcmcia_replace_cis(s->parent, &buf->cisdump); - break; - case DS_BIND_REQUEST: - if (!capable(CAP_SYS_ADMIN)) { - err = -EPERM; - goto free_out; - } - err = bind_request(s, &buf->bind_info); - break; - case DS_GET_DEVICE_INFO: - err = get_device_info(s, &buf->bind_info, 1); - break; - case DS_GET_NEXT_DEVICE: - err = get_device_info(s, &buf->bind_info, 0); - break; - case DS_UNBIND_REQUEST: - err = 0; - break; - default: - err = -EINVAL; - } - - if ((err == 0) && (ret != CS_SUCCESS)) { - ds_dbg(2, "ds_ioctl: ret = %d\n", ret); - switch (ret) { - case CS_BAD_SOCKET: case CS_NO_CARD: - err = -ENODEV; break; - case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: - case CS_BAD_TUPLE: - err = -EINVAL; break; - case CS_IN_USE: - err = -EBUSY; break; - case CS_OUT_OF_RESOURCE: - err = -ENOSPC; break; - case CS_NO_MORE_ITEMS: - err = -ENODATA; break; - case CS_UNSUPPORTED_FUNCTION: - err = -ENOSYS; break; - default: - err = -EIO; break; - } - } - - if (cmd & IOC_OUT) { - if (__copy_to_user(uarg, (char *)buf, size)) - err = -EFAULT; - } - -free_out: - kfree(buf); - return err; -} /* ds_ioctl */ - -/*====================================================================*/ - -static struct file_operations ds_fops = { - .owner = THIS_MODULE, - .open = ds_open, - .release = ds_release, - .ioctl = ds_ioctl, - .read = ds_read, - .write = ds_write, - .poll = ds_poll, +static struct pcmcia_callback pcmcia_bus_callback = { + .owner = THIS_MODULE, + .event = ds_event, + .requery = pcmcia_bus_rescan, }; static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - struct pcmcia_bus_socket *s; int ret; - s = kmalloc(sizeof(struct pcmcia_bus_socket), GFP_KERNEL); - if(!s) - return -ENOMEM; - memset(s, 0, sizeof(struct pcmcia_bus_socket)); - - /* get reference to parent socket */ - s->parent = pcmcia_get_socket(socket); - if (!s->parent) { + socket = pcmcia_get_socket(socket); + if (!socket) { printk(KERN_ERR "PCMCIA obtaining reference to socket %p failed\n", socket); - kfree (s); return -ENODEV; } - kref_init(&s->refcount); - /* * Ugly. But we want to wait for the socket threads to have started up. * We really should let the drivers themselves drive some of this.. */ msleep(250); - init_waitqueue_head(&s->queue); - INIT_LIST_HEAD(&s->devices_list); - - /* Set up hotline to Card Services */ - s->callback.owner = THIS_MODULE; - s->callback.event = &ds_event; - s->callback.resources_done = &pcmcia_card_add; - socket->pcmcia = s; +#ifdef CONFIG_PCMCIA_IOCTL + init_waitqueue_head(&socket->queue); +#endif + INIT_LIST_HEAD(&socket->devices_list); + INIT_WORK(&socket->device_add, pcmcia_delayed_add_pseudo_device, socket); + memset(&socket->pcmcia_state, 0, sizeof(u8)); + socket->device_count = 0; - ret = pccard_register_pcmcia(socket, &s->callback); + ret = pccard_register_pcmcia(socket, &pcmcia_bus_callback); if (ret) { printk(KERN_ERR "PCMCIA registration PCCard core failed for socket %p\n", socket); - pcmcia_put_bus_socket(s); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return (ret); } return 0; } - static void pcmcia_bus_remove_socket(struct class_device *class_dev) { struct pcmcia_socket *socket = class_get_devdata(class_dev); - if (!socket || !socket->pcmcia) + if (!socket) return; + socket->pcmcia_state.dead = 1; pccard_register_pcmcia(socket, NULL); - socket->pcmcia->state |= DS_SOCKET_DEAD; - pcmcia_put_bus_socket(socket->pcmcia); - socket->pcmcia = NULL; + pcmcia_put_socket(socket); return; } @@ -1575,34 +1209,20 @@ static struct class_interface pcmcia_bus_interface = { struct bus_type pcmcia_bus_type = { .name = "pcmcia", + .hotplug = pcmcia_bus_hotplug, .match = pcmcia_bus_match, .dev_attrs = pcmcia_dev_attrs, }; -EXPORT_SYMBOL(pcmcia_bus_type); static int __init init_pcmcia_bus(void) { - int i; - spin_lock_init(&pcmcia_dev_list_lock); bus_register(&pcmcia_bus_type); class_interface_register(&pcmcia_bus_interface); - /* Set up character device for user mode clients */ - i = register_chrdev(0, "pcmcia", &ds_fops); - if (i < 0) - printk(KERN_NOTICE "unable to find a free device # for " - "Driver Services (error=%d)\n", i); - else - major_dev = i; - -#ifdef CONFIG_PROC_FS - proc_pccard = proc_mkdir("pccard", proc_bus); - if (proc_pccard) - create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL); -#endif + pcmcia_setup_ioctl(); return 0; } @@ -1612,48 +1232,13 @@ fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that static void __exit exit_pcmcia_bus(void) { - class_interface_unregister(&pcmcia_bus_interface); + pcmcia_cleanup_ioctl(); -#ifdef CONFIG_PROC_FS - if (proc_pccard) { - remove_proc_entry("drivers", proc_pccard); - remove_proc_entry("pccard", proc_bus); - } -#endif - if (major_dev != -1) - unregister_chrdev(major_dev, "pcmcia"); + class_interface_unregister(&pcmcia_bus_interface); bus_unregister(&pcmcia_bus_type); } module_exit(exit_pcmcia_bus); - -/* helpers for backwards-compatible functions */ - -static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr) -{ - struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr); - if (s && s->pcmcia) - return s->pcmcia; - else - return NULL; -} - -/* backwards-compatible accessing of driver --- by name! */ - -static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info) -{ - struct device_driver *drv; - struct pcmcia_driver *p_drv; - - drv = driver_find((char *) dev_info, &pcmcia_bus_type); - if (!drv) - return NULL; - - p_drv = container_of(drv, struct pcmcia_driver, drv); - - return (p_drv); -} - MODULE_ALIAS("ds"); diff --git a/drivers/pcmcia/ds_internal.h b/drivers/pcmcia/ds_internal.h new file mode 100644 index 00000000000..d359bd25a51 --- /dev/null +++ b/drivers/pcmcia/ds_internal.h @@ -0,0 +1,21 @@ +/* ds_internal.h - internal header for 16-bit PCMCIA devices management */ + +extern spinlock_t pcmcia_dev_list_lock; +extern struct bus_type pcmcia_bus_type; + +extern struct pcmcia_device * pcmcia_get_dev(struct pcmcia_device *p_dev); +extern void pcmcia_put_dev(struct pcmcia_device *p_dev); + +struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function); + +#ifdef CONFIG_PCMCIA_IOCTL +extern void __init pcmcia_setup_ioctl(void); +extern void __exit pcmcia_cleanup_ioctl(void); +extern void handle_event(struct pcmcia_socket *s, event_t event); +extern int handle_request(struct pcmcia_socket *s, event_t event); +#else +static inline void __init pcmcia_setup_ioctl(void) { return; } +static inline void __init pcmcia_cleanup_ioctl(void) { return; } +static inline void handle_event(struct pcmcia_socket *s, event_t event) { return; } +static inline int handle_request(struct pcmcia_socket *s, event_t event) { return CS_SUCCESS; } +#endif diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 90a335a5d9f..d72f9a35c8b 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -669,11 +669,13 @@ static int __init is_alive(u_short sock) if ((stat & I365_CS_DETECT) && (stat & I365_CS_POWERON) && (i365_get(sock, I365_INTCTL) & I365_PC_IOCARD) && (i365_get(sock, I365_ADDRWIN) & I365_ENA_IO(0)) && - (check_region(start, stop-start+1) != 0) && - ((start & 0xfeef) != 0x02e8)) - return 1; - else - return 0; + ((start & 0xfeef) != 0x02e8)) { + if (!request_region(start, stop-start+1, "i82365")) + return 1; + release_region(start, stop-start+1); + } + + return 0; } /*====================================================================*/ @@ -696,7 +698,13 @@ static void __init add_pcic(int ns, int type) struct i82365_socket *t = &socket[sockets-ns]; base = sockets-ns; - if (t->ioaddr > 0) request_region(t->ioaddr, 2, "i82365"); + if (t->ioaddr > 0) { + if (!request_region(t->ioaddr, 2, "i82365")) { + printk(KERN_ERR "i82365: IO region conflict at %#lx, not available\n", + t->ioaddr); + return; + } + } if (base == 0) printk("\n"); printk(KERN_INFO " %s", pcic[type].name); @@ -803,7 +811,7 @@ static void __init isa_probe(void) } #endif - if (check_region(i365_base, 2) != 0) { + if (!request_region(i365_base, 2, "i82365")) { if (sockets == 0) printk("port conflict at %#lx\n", i365_base); return; @@ -1441,6 +1449,7 @@ static void __exit exit_i82365(void) i365_set(i, I365_CSCINT, 0); release_region(socket[i].ioaddr, 2); } + release_region(i365_base, 2); #ifdef CONFIG_PNP if (i82365_pnpdev) pnp_disable_dev(i82365_pnpdev); diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c index 68b80084f83..1cc83317e7e 100644 --- a/drivers/pcmcia/pcmcia_compat.c +++ b/drivers/pcmcia/pcmcia_compat.c @@ -74,19 +74,6 @@ int pcmcia_validate_cis(client_handle_t handle, cisinfo_t *info) } EXPORT_SYMBOL(pcmcia_validate_cis); -int pcmcia_get_configuration_info(client_handle_t handle, - config_info_t *config) -{ - struct pcmcia_socket *s; - - if ((CHECK_HANDLE(handle)) || !config) - return CS_BAD_HANDLE; - s = SOCKET(handle); - if (!s) - return CS_BAD_HANDLE; - return pccard_get_configuration_info(s, handle->Function, config); -} -EXPORT_SYMBOL(pcmcia_get_configuration_info); int pcmcia_reset_card(client_handle_t handle, client_req_t *req) { @@ -102,24 +89,3 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) } EXPORT_SYMBOL(pcmcia_reset_card); -int pcmcia_get_status(client_handle_t handle, cs_status_t *status) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_get_status(s, handle->Function, status); -} -EXPORT_SYMBOL(pcmcia_get_status); - -int pcmcia_access_configuration_register(client_handle_t handle, - conf_reg_t *reg) -{ - struct pcmcia_socket *s; - if (CHECK_HANDLE(handle)) - return CS_BAD_HANDLE; - s = SOCKET(handle); - return pccard_access_configuration_register(s, handle->Function, reg); -} -EXPORT_SYMBOL(pcmcia_access_configuration_register); - diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c new file mode 100644 index 00000000000..b883bc151ed --- /dev/null +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -0,0 +1,786 @@ +/* + * pcmcia_ioctl.c -- ioctl interface for cardmgr and cardctl + * + * 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 + * published by the Free Software Foundation. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + * (C) 2003 - 2004 Dominik Brodowski + */ + +/* + * This file will go away soon. + */ + + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/ioctl.h> +#include <linux/proc_fs.h> +#include <linux/poll.h> +#include <linux/pci.h> +#include <linux/workqueue.h> + +#define IN_CARD_SERVICES +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <pcmcia/ss.h> + +#include "cs_internal.h" +#include "ds_internal.h" + +static int major_dev = -1; + + +/* Device user information */ +#define MAX_EVENTS 32 +#define USER_MAGIC 0x7ea4 +#define CHECK_USER(u) \ + (((u) == NULL) || ((u)->user_magic != USER_MAGIC)) + +typedef struct user_info_t { + u_int user_magic; + int event_head, event_tail; + event_t event[MAX_EVENTS]; + struct user_info_t *next; + struct pcmcia_socket *socket; +} user_info_t; + + +#ifdef DEBUG +extern int ds_pc_debug; +#define cs_socket_name(skt) ((skt)->dev.class_id) + +#define ds_dbg(lvl, fmt, arg...) do { \ + if (ds_pc_debug >= lvl) \ + printk(KERN_DEBUG "ds: " fmt , ## arg); \ +} while (0) +#else +#define ds_dbg(lvl, fmt, arg...) do { } while (0) +#endif + +static const char *release = "Linux Kernel Card Services"; + +/** pcmcia_get_card_services_info + * + * Return information about this version of Card Services + */ +static int pcmcia_get_card_services_info(servinfo_t *info) +{ + unsigned int socket_count = 0; + struct list_head *tmp; + info->Signature[0] = 'C'; + info->Signature[1] = 'S'; + down_read(&pcmcia_socket_list_rwsem); + list_for_each(tmp, &pcmcia_socket_list) + socket_count++; + up_read(&pcmcia_socket_list_rwsem); + info->Count = socket_count; + info->Revision = CS_RELEASE_CODE; + info->CSLevel = 0x0210; + info->VendorString = (char *)release; + return CS_SUCCESS; +} /* get_card_services_info */ + + +/* backwards-compatible accessing of driver --- by name! */ + +static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info) +{ + struct device_driver *drv; + struct pcmcia_driver *p_drv; + + drv = driver_find((char *) dev_info, &pcmcia_bus_type); + if (!drv) + return NULL; + + p_drv = container_of(drv, struct pcmcia_driver, drv); + + return (p_drv); +} + + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_pccard = NULL; + +static int proc_read_drivers_callback(struct device_driver *driver, void *d) +{ + char **p = d; + struct pcmcia_driver *p_drv = container_of(driver, + struct pcmcia_driver, drv); + + *p += sprintf(*p, "%-24.24s 1 %d\n", p_drv->drv.name, +#ifdef CONFIG_MODULE_UNLOAD + (p_drv->owner) ? module_refcount(p_drv->owner) : 1 +#else + 1 +#endif + ); + d = (void *) p; + + return 0; +} + +static int proc_read_drivers(char *buf, char **start, off_t pos, + int count, int *eof, void *data) +{ + char *p = buf; + + bus_for_each_drv(&pcmcia_bus_type, NULL, + (void *) &p, proc_read_drivers_callback); + + return (p - buf); +} +#endif + +/*====================================================================== + + These manage a ring buffer of events pending for one user process + +======================================================================*/ + + +static int queue_empty(user_info_t *user) +{ + return (user->event_head == user->event_tail); +} + +static event_t get_queued_event(user_info_t *user) +{ + user->event_tail = (user->event_tail+1) % MAX_EVENTS; + return user->event[user->event_tail]; +} + +static void queue_event(user_info_t *user, event_t event) +{ + user->event_head = (user->event_head+1) % MAX_EVENTS; + if (user->event_head == user->event_tail) + user->event_tail = (user->event_tail+1) % MAX_EVENTS; + user->event[user->event_head] = event; +} + +void handle_event(struct pcmcia_socket *s, event_t event) +{ + user_info_t *user; + for (user = s->user; user; user = user->next) + queue_event(user, event); + wake_up_interruptible(&s->queue); +} + + +/*====================================================================== + + bind_request() and bind_device() are merged by now. Register_client() + is called right at the end of bind_request(), during the driver's + ->attach() call. Individual descriptions: + + bind_request() connects a socket to a particular client driver. + It looks up the specified device ID in the list of registered + drivers, binds it to the socket, and tries to create an instance + of the device. unbind_request() deletes a driver instance. + + Bind_device() associates a device driver with a particular socket. + It is normally called by Driver Services after it has identified + a newly inserted card. An instance of that driver will then be + eligible to register as a client of this socket. + + Register_client() uses the dev_info_t handle to match the + caller with a socket. The driver must have already been bound + to a socket with bind_device() -- in fact, bind_device() + allocates the client structure that will be used. + +======================================================================*/ + +static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) +{ + struct pcmcia_driver *p_drv; + struct pcmcia_device *p_dev; + int ret = 0; + unsigned long flags; + + s = pcmcia_get_socket(s); + if (!s) + return -EINVAL; + + ds_dbg(2, "bind_request(%d, '%s')\n", s->sock, + (char *)bind_info->dev_info); + + p_drv = get_pcmcia_driver(&bind_info->dev_info); + if (!p_drv) { + ret = -EINVAL; + goto err_put; + } + + if (!try_module_get(p_drv->owner)) { + ret = -EINVAL; + goto err_put_driver; + } + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { + if (p_dev->func == bind_info->function) { + if ((p_dev->dev.driver == &p_drv->drv)) { + if (p_dev->cardmgr) { + /* if there's already a device + * registered, and it was registered + * by userspace before, we need to + * return the "instance". */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + bind_info->instance = p_dev->instance; + ret = -EBUSY; + goto err_put_module; + } else { + /* the correct driver managed to bind + * itself magically to the correct + * device. */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + p_dev->cardmgr = p_drv; + ret = 0; + goto err_put_module; + } + } else if (!p_dev->dev.driver) { + /* there's already a device available where + * no device has been bound to yet. So we don't + * need to register a device! */ + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + goto rescan; + } + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + p_dev = pcmcia_device_add(s, bind_info->function); + if (!p_dev) { + ret = -EIO; + goto err_put_module; + } + +rescan: + p_dev->cardmgr = p_drv; + + /* if a driver is already running, we can abort */ + if (p_dev->dev.driver) + goto err_put_module; + + /* + * Prevent this racing with a card insertion. + */ + down(&s->skt_sem); + bus_rescan_devices(&pcmcia_bus_type); + up(&s->skt_sem); + + /* check whether the driver indeed matched. I don't care if this + * is racy or not, because it can only happen on cardmgr access + * paths... + */ + if (!(p_dev->dev.driver == &p_drv->drv)) + p_dev->cardmgr = NULL; + + err_put_module: + module_put(p_drv->owner); + err_put_driver: + put_driver(&p_drv->drv); + err_put: + pcmcia_put_socket(s); + + return (ret); +} /* bind_request */ + +#ifdef CONFIG_CARDBUS + +static struct pci_bus *pcmcia_lookup_bus(struct pcmcia_socket *s) +{ + if (!s || !(s->state & SOCKET_CARDBUS)) + return NULL; + + return s->cb_dev->subordinate; +} +#endif + +static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int first) +{ + dev_node_t *node; + struct pcmcia_device *p_dev; + unsigned long flags; + int ret = 0; + +#ifdef CONFIG_CARDBUS + /* + * Some unbelievably ugly code to associate the PCI cardbus + * device and its driver with the PCMCIA "bind" information. + */ + { + struct pci_bus *bus; + + bus = pcmcia_lookup_bus(s); + if (bus) { + struct list_head *list; + struct pci_dev *dev = NULL; + + list = bus->devices.next; + while (list != &bus->devices) { + struct pci_dev *pdev = pci_dev_b(list); + list = list->next; + + if (first) { + dev = pdev; + break; + } + + /* Try to handle "next" here some way? */ + } + if (dev && dev->driver) { + strlcpy(bind_info->name, dev->driver->name, DEV_NAME_LEN); + bind_info->major = 0; + bind_info->minor = 0; + bind_info->next = NULL; + return 0; + } + } + } +#endif + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { + if (p_dev->func == bind_info->function) { + p_dev = pcmcia_get_dev(p_dev); + if (!p_dev) + continue; + goto found; + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return -ENODEV; + + found: + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + if ((!p_dev->instance) || + (p_dev->instance->state & DEV_CONFIG_PENDING)) { + ret = -EAGAIN; + goto err_put; + } + + if (first) + node = p_dev->instance->dev; + else + for (node = p_dev->instance->dev; node; node = node->next) + if (node == bind_info->next) + break; + if (!node) { + ret = -ENODEV; + goto err_put; + } + + strlcpy(bind_info->name, node->dev_name, DEV_NAME_LEN); + bind_info->major = node->major; + bind_info->minor = node->minor; + bind_info->next = node->next; + + err_put: + pcmcia_put_dev(p_dev); + return (ret); +} /* get_device_info */ + + +static int ds_open(struct inode *inode, struct file *file) +{ + socket_t i = iminor(inode); + struct pcmcia_socket *s; + user_info_t *user; + + ds_dbg(0, "ds_open(socket %d)\n", i); + + s = pcmcia_get_socket_by_nr(i); + if (!s) + return -ENODEV; + s = pcmcia_get_socket(s); + if (!s) + return -ENODEV; + + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (s->pcmcia_state.busy) { + pcmcia_put_socket(s); + return -EBUSY; + } + else + s->pcmcia_state.busy = 1; + } + + user = kmalloc(sizeof(user_info_t), GFP_KERNEL); + if (!user) { + pcmcia_put_socket(s); + return -ENOMEM; + } + user->event_tail = user->event_head = 0; + user->next = s->user; + user->user_magic = USER_MAGIC; + user->socket = s; + s->user = user; + file->private_data = user; + + if (s->pcmcia_state.present) + queue_event(user, CS_EVENT_CARD_INSERTION); + return 0; +} /* ds_open */ + +/*====================================================================*/ + +static int ds_release(struct inode *inode, struct file *file) +{ + struct pcmcia_socket *s; + user_info_t *user, **link; + + ds_dbg(0, "ds_release(socket %d)\n", iminor(inode)); + + user = file->private_data; + if (CHECK_USER(user)) + goto out; + + s = user->socket; + + /* Unlink user data structure */ + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + s->pcmcia_state.busy = 0; + } + file->private_data = NULL; + for (link = &s->user; *link; link = &(*link)->next) + if (*link == user) break; + if (link == NULL) + goto out; + *link = user->next; + user->user_magic = 0; + kfree(user); + pcmcia_put_socket(s); +out: + return 0; +} /* ds_release */ + +/*====================================================================*/ + +static ssize_t ds_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pcmcia_socket *s; + user_info_t *user; + int ret; + + ds_dbg(2, "ds_read(socket %d)\n", iminor(file->f_dentry->d_inode)); + + if (count < 4) + return -EINVAL; + + user = file->private_data; + if (CHECK_USER(user)) + return -EIO; + + s = user->socket; + if (s->pcmcia_state.dead) + return -EIO; + + ret = wait_event_interruptible(s->queue, !queue_empty(user)); + if (ret == 0) + ret = put_user(get_queued_event(user), (int __user *)buf) ? -EFAULT : 4; + + return ret; +} /* ds_read */ + +/*====================================================================*/ + +static ssize_t ds_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + ds_dbg(2, "ds_write(socket %d)\n", iminor(file->f_dentry->d_inode)); + + if (count != 4) + return -EINVAL; + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EBADF; + + return -EIO; +} /* ds_write */ + +/*====================================================================*/ + +/* No kernel lock - fine */ +static u_int ds_poll(struct file *file, poll_table *wait) +{ + struct pcmcia_socket *s; + user_info_t *user; + + ds_dbg(2, "ds_poll(socket %d)\n", iminor(file->f_dentry->d_inode)); + + user = file->private_data; + if (CHECK_USER(user)) + return POLLERR; + s = user->socket; + /* + * We don't check for a dead socket here since that + * will send cardmgr into an endless spin. + */ + poll_wait(file, &s->queue, wait); + if (!queue_empty(user)) + return POLLIN | POLLRDNORM; + return 0; +} /* ds_poll */ + +/*====================================================================*/ + +extern int pcmcia_adjust_resource_info(adjust_t *adj); + +static int ds_ioctl(struct inode * inode, struct file * file, + u_int cmd, u_long arg) +{ + struct pcmcia_socket *s; + void __user *uarg = (char __user *)arg; + u_int size; + int ret, err; + ds_ioctl_arg_t *buf; + user_info_t *user; + + ds_dbg(2, "ds_ioctl(socket %d, %#x, %#lx)\n", iminor(inode), cmd, arg); + + user = file->private_data; + if (CHECK_USER(user)) + return -EIO; + + s = user->socket; + if (s->pcmcia_state.dead) + return -EIO; + + size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT; + if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL; + + /* Permission check */ + if (!(cmd & IOC_OUT) && !capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (cmd & IOC_IN) { + if (!access_ok(VERIFY_READ, uarg, size)) { + ds_dbg(3, "ds_ioctl(): verify_read = %d\n", -EFAULT); + return -EFAULT; + } + } + if (cmd & IOC_OUT) { + if (!access_ok(VERIFY_WRITE, uarg, size)) { + ds_dbg(3, "ds_ioctl(): verify_write = %d\n", -EFAULT); + return -EFAULT; + } + } + buf = kmalloc(sizeof(ds_ioctl_arg_t), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = ret = 0; + + if (cmd & IOC_IN) __copy_from_user((char *)buf, uarg, size); + + switch (cmd) { + case DS_ADJUST_RESOURCE_INFO: + ret = pcmcia_adjust_resource_info(&buf->adjust); + break; + case DS_GET_CARD_SERVICES_INFO: + ret = pcmcia_get_card_services_info(&buf->servinfo); + break; + case DS_GET_CONFIGURATION_INFO: + if (buf->config.Function && + (buf->config.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_get_configuration_info(s, + buf->config.Function, &buf->config); + break; + case DS_GET_FIRST_TUPLE: + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple); + break; + case DS_GET_NEXT_TUPLE: + ret = pccard_get_next_tuple(s, BIND_FN_ALL, &buf->tuple); + break; + case DS_GET_TUPLE_DATA: + buf->tuple.TupleData = buf->tuple_parse.data; + buf->tuple.TupleDataMax = sizeof(buf->tuple_parse.data); + ret = pccard_get_tuple_data(s, &buf->tuple); + break; + case DS_PARSE_TUPLE: + buf->tuple.TupleData = buf->tuple_parse.data; + ret = pccard_parse_tuple(&buf->tuple, &buf->tuple_parse.parse); + break; + case DS_RESET_CARD: + ret = pccard_reset_card(s); + break; + case DS_GET_STATUS: + if (buf->status.Function && + (buf->status.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_get_status(s, buf->status.Function, &buf->status); + break; + case DS_VALIDATE_CIS: + down(&s->skt_sem); + pcmcia_validate_mem(s); + up(&s->skt_sem); + ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo); + break; + case DS_SUSPEND_CARD: + ret = pcmcia_suspend_card(s); + break; + case DS_RESUME_CARD: + ret = pcmcia_resume_card(s); + break; + case DS_EJECT_CARD: + err = pcmcia_eject_card(s); + break; + case DS_INSERT_CARD: + err = pcmcia_insert_card(s); + break; + case DS_ACCESS_CONFIGURATION_REGISTER: + if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto free_out; + } + if (buf->conf_reg.Function && + (buf->conf_reg.Function >= s->functions)) + ret = CS_BAD_ARGS; + else + ret = pccard_access_configuration_register(s, + buf->conf_reg.Function, &buf->conf_reg); + break; + case DS_GET_FIRST_REGION: + case DS_GET_NEXT_REGION: + case DS_BIND_MTD: + if (!capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto free_out; + } else { + static int printed = 0; + if (!printed) { + printk(KERN_WARNING "2.6. kernels use pcmciamtd instead of memory_cs.c and do not require special\n"); + printk(KERN_WARNING "MTD handling any more.\n"); + printed++; + } + } + err = -EINVAL; + goto free_out; + break; + case DS_GET_FIRST_WINDOW: + ret = pcmcia_get_window(s, &buf->win_info.handle, 0, + &buf->win_info.window); + break; + case DS_GET_NEXT_WINDOW: + ret = pcmcia_get_window(s, &buf->win_info.handle, + buf->win_info.handle->index + 1, &buf->win_info.window); + break; + case DS_GET_MEM_PAGE: + ret = pcmcia_get_mem_page(buf->win_info.handle, + &buf->win_info.map); + break; + case DS_REPLACE_CIS: + ret = pcmcia_replace_cis(s, &buf->cisdump); + break; + case DS_BIND_REQUEST: + if (!capable(CAP_SYS_ADMIN)) { + err = -EPERM; + goto free_out; + } + err = bind_request(s, &buf->bind_info); + break; + case DS_GET_DEVICE_INFO: + err = get_device_info(s, &buf->bind_info, 1); + break; + case DS_GET_NEXT_DEVICE: + err = get_device_info(s, &buf->bind_info, 0); + break; + case DS_UNBIND_REQUEST: + err = 0; + break; + default: + err = -EINVAL; + } + + if ((err == 0) && (ret != CS_SUCCESS)) { + ds_dbg(2, "ds_ioctl: ret = %d\n", ret); + switch (ret) { + case CS_BAD_SOCKET: case CS_NO_CARD: + err = -ENODEV; break; + case CS_BAD_ARGS: case CS_BAD_ATTRIBUTE: case CS_BAD_IRQ: + case CS_BAD_TUPLE: + err = -EINVAL; break; + case CS_IN_USE: + err = -EBUSY; break; + case CS_OUT_OF_RESOURCE: + err = -ENOSPC; break; + case CS_NO_MORE_ITEMS: + err = -ENODATA; break; + case CS_UNSUPPORTED_FUNCTION: + err = -ENOSYS; break; + default: + err = -EIO; break; + } + } + + if (cmd & IOC_OUT) { + if (__copy_to_user(uarg, (char *)buf, size)) + err = -EFAULT; + } + +free_out: + kfree(buf); + return err; +} /* ds_ioctl */ + +/*====================================================================*/ + +static struct file_operations ds_fops = { + .owner = THIS_MODULE, + .open = ds_open, + .release = ds_release, + .ioctl = ds_ioctl, + .read = ds_read, + .write = ds_write, + .poll = ds_poll, +}; + +void __init pcmcia_setup_ioctl(void) { + int i; + + /* Set up character device for user mode clients */ + i = register_chrdev(0, "pcmcia", &ds_fops); + if (i < 0) + printk(KERN_NOTICE "unable to find a free device # for " + "Driver Services (error=%d)\n", i); + else + major_dev = i; + +#ifdef CONFIG_PROC_FS + proc_pccard = proc_mkdir("pccard", proc_bus); + if (proc_pccard) + create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL); +#endif +} + + +void __exit pcmcia_cleanup_ioctl(void) { +#ifdef CONFIG_PROC_FS + if (proc_pccard) { + remove_proc_entry("drivers", proc_pccard); + remove_proc_entry("pccard", proc_bus); + } +#endif + if (major_dev != -1) + unregister_chrdev(major_dev, "pcmcia"); +} diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c new file mode 100644 index 00000000000..c01dc6bf152 --- /dev/null +++ b/drivers/pcmcia/pcmcia_resource.c @@ -0,0 +1,998 @@ +/* + * PCMCIA 16-bit resource management functions + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Copyright (C) 1999 David A. Hinds + * Copyright (C) 2004-2005 Dominik Brodowski + * + * 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 + * published by the Free Software Foundation. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/device.h> + +#define IN_CARD_SERVICES +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/ss.h> +#include <pcmcia/cs.h> +#include <pcmcia/bulkmem.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ds.h> + +#include "cs_internal.h" +#include "ds_internal.h" + + +/* Access speed for IO windows */ +static int io_speed = 0; +module_param(io_speed, int, 0444); + + +#ifdef CONFIG_PCMCIA_PROBE +/* mask of IRQs already reserved by other cards, we should avoid using them */ +static u8 pcmcia_used_irq[NR_IRQS]; +#endif + + +#ifdef DEBUG +extern int ds_pc_debug; +#define cs_socket_name(skt) ((skt)->dev.class_id) + +#define ds_dbg(skt, lvl, fmt, arg...) do { \ + if (ds_pc_debug >= lvl) \ + printk(KERN_DEBUG "pcmcia_resource: %s: " fmt, \ + cs_socket_name(skt) , ## arg); \ +} while (0) +#else +#define ds_dbg(lvl, fmt, arg...) do { } while (0) +#endif + + + +/** alloc_io_space + * + * Special stuff for managing IO windows, because they are scarce + */ + +static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, + ioaddr_t num, u_int lines) +{ + int i; + kio_addr_t try, align; + + align = (*base) ? (lines ? 1<<lines : 0) : 1; + if (align && (align < num)) { + if (*base) { + ds_dbg(s, 0, "odd IO request: num %#x align %#lx\n", + num, align); + align = 0; + } else + while (align && (align < num)) align <<= 1; + } + if (*base & ~(align-1)) { + ds_dbg(s, 0, "odd IO request: base %#x align %#lx\n", + *base, align); + align = 0; + } + if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { + *base = s->io_offset | (*base & 0x0fff); + s->io[0].Attributes = attr; + return 0; + } + /* Check for an already-allocated window that must conflict with + * what was asked for. It is a hack because it does not catch all + * potential conflicts, just the most obvious ones. + */ + for (i = 0; i < MAX_IO_WIN; i++) + if ((s->io[i].NumPorts != 0) && + ((s->io[i].BasePort & (align-1)) == *base)) + return 1; + for (i = 0; i < MAX_IO_WIN; i++) { + if (s->io[i].NumPorts == 0) { + s->io[i].res = pcmcia_find_io_region(*base, num, align, s); + if (s->io[i].res) { + s->io[i].Attributes = attr; + s->io[i].BasePort = *base = s->io[i].res->start; + s->io[i].NumPorts = s->io[i].InUse = num; + break; + } else + return 1; + } else if (s->io[i].Attributes != attr) + continue; + /* Try to extend top of window */ + try = s->io[i].BasePort + s->io[i].NumPorts; + if ((*base == 0) || (*base == try)) + if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, + s->io[i].res->end + num, s) == 0) { + *base = try; + s->io[i].NumPorts += num; + s->io[i].InUse += num; + break; + } + /* Try to extend bottom of window */ + try = s->io[i].BasePort - num; + if ((*base == 0) || (*base == try)) + if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, + s->io[i].res->end, s) == 0) { + s->io[i].BasePort = *base = try; + s->io[i].NumPorts += num; + s->io[i].InUse += num; + break; + } + } + return (i == MAX_IO_WIN); +} /* alloc_io_space */ + + +static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, + ioaddr_t num) +{ + int i; + + for (i = 0; i < MAX_IO_WIN; i++) { + if ((s->io[i].BasePort <= base) && + (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { + s->io[i].InUse -= num; + /* Free the window if no one else is using it */ + if (s->io[i].InUse == 0) { + s->io[i].NumPorts = 0; + release_resource(s->io[i].res); + kfree(s->io[i].res); + s->io[i].res = NULL; + } + } + } +} /* release_io_space */ + + +/** pccard_access_configuration_register + * + * Access_configuration_register() reads and writes configuration + * registers in attribute memory. Memory window 0 is reserved for + * this and the tuple reading services. + */ + +int pccard_access_configuration_register(struct pcmcia_socket *s, + unsigned int function, + conf_reg_t *reg) +{ + config_t *c; + int addr; + u_char val; + + if (!s || !s->config) + return CS_NO_CARD; + + c = &s->config[function]; + + if (c == NULL) + return CS_NO_CARD; + + if (!(c->state & CONFIG_LOCKED)) + return CS_CONFIGURATION_LOCKED; + + addr = (c->ConfigBase + reg->Offset) >> 1; + + switch (reg->Action) { + case CS_READ: + pcmcia_read_cis_mem(s, 1, addr, 1, &val); + reg->Value = val; + break; + case CS_WRITE: + val = reg->Value; + pcmcia_write_cis_mem(s, 1, addr, 1, &val); + break; + default: + return CS_BAD_ARGS; + break; + } + return CS_SUCCESS; +} /* pccard_access_configuration_register */ + +int pcmcia_access_configuration_register(client_handle_t handle, + conf_reg_t *reg) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_access_configuration_register(s, handle->Function, reg); +} +EXPORT_SYMBOL(pcmcia_access_configuration_register); + + + +int pccard_get_configuration_info(struct pcmcia_socket *s, + unsigned int function, + config_info_t *config) +{ + config_t *c; + + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + config->Function = function; + +#ifdef CONFIG_CARDBUS + if (s->state & SOCKET_CARDBUS) { + memset(config, 0, sizeof(config_info_t)); + config->Vcc = s->socket.Vcc; + config->Vpp1 = config->Vpp2 = s->socket.Vpp; + config->Option = s->cb_dev->subordinate->number; + if (s->state & SOCKET_CARDBUS_CONFIG) { + config->Attributes = CONF_VALID_CLIENT; + config->IntType = INT_CARDBUS; + config->AssignedIRQ = s->irq.AssignedIRQ; + if (config->AssignedIRQ) + config->Attributes |= CONF_ENABLE_IRQ; + config->BasePort1 = s->io[0].BasePort; + config->NumPorts1 = s->io[0].NumPorts; + } + return CS_SUCCESS; + } +#endif + + c = (s->config != NULL) ? &s->config[function] : NULL; + + if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { + config->Attributes = 0; + config->Vcc = s->socket.Vcc; + config->Vpp1 = config->Vpp2 = s->socket.Vpp; + return CS_SUCCESS; + } + + /* !!! This is a hack !!! */ + memcpy(&config->Attributes, &c->Attributes, sizeof(config_t)); + config->Attributes |= CONF_VALID_CLIENT; + config->CardValues = c->CardValues; + config->IRQAttributes = c->irq.Attributes; + config->AssignedIRQ = s->irq.AssignedIRQ; + config->BasePort1 = c->io.BasePort1; + config->NumPorts1 = c->io.NumPorts1; + config->Attributes1 = c->io.Attributes1; + config->BasePort2 = c->io.BasePort2; + config->NumPorts2 = c->io.NumPorts2; + config->Attributes2 = c->io.Attributes2; + config->IOAddrLines = c->io.IOAddrLines; + + return CS_SUCCESS; +} /* pccard_get_configuration_info */ + +int pcmcia_get_configuration_info(client_handle_t handle, + config_info_t *config) +{ + struct pcmcia_socket *s; + + if ((CHECK_HANDLE(handle)) || !config) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!s) + return CS_BAD_HANDLE; + return pccard_get_configuration_info(s, handle->Function, config); +} +EXPORT_SYMBOL(pcmcia_get_configuration_info); + + +/** pcmcia_get_window + */ +int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, + int idx, win_req_t *req) +{ + window_t *win; + int w; + + if (!s || !(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + for (w = idx; w < MAX_WIN; w++) + if (s->state & SOCKET_WIN_REQ(w)) + break; + if (w == MAX_WIN) + return CS_NO_MORE_ITEMS; + win = &s->win[w]; + req->Base = win->ctl.res->start; + req->Size = win->ctl.res->end - win->ctl.res->start + 1; + req->AccessSpeed = win->ctl.speed; + req->Attributes = 0; + if (win->ctl.flags & MAP_ATTRIB) + req->Attributes |= WIN_MEMORY_TYPE_AM; + if (win->ctl.flags & MAP_ACTIVE) + req->Attributes |= WIN_ENABLE; + if (win->ctl.flags & MAP_16BIT) + req->Attributes |= WIN_DATA_WIDTH_16; + if (win->ctl.flags & MAP_USE_WAIT) + req->Attributes |= WIN_USE_WAIT; + *handle = win; + return CS_SUCCESS; +} /* pcmcia_get_window */ +EXPORT_SYMBOL(pcmcia_get_window); + + +/** pccard_get_status + * + * Get the current socket state bits. We don't support the latched + * SocketState yet: I haven't seen any point for it. + */ + +int pccard_get_status(struct pcmcia_socket *s, unsigned int function, + cs_status_t *status) +{ + config_t *c; + int val; + + s->ops->get_status(s, &val); + status->CardState = status->SocketState = 0; + status->CardState |= (val & SS_DETECT) ? CS_EVENT_CARD_DETECT : 0; + status->CardState |= (val & SS_CARDBUS) ? CS_EVENT_CB_DETECT : 0; + status->CardState |= (val & SS_3VCARD) ? CS_EVENT_3VCARD : 0; + status->CardState |= (val & SS_XVCARD) ? CS_EVENT_XVCARD : 0; + if (s->state & SOCKET_SUSPEND) + status->CardState |= CS_EVENT_PM_SUSPEND; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + c = (s->config != NULL) ? &s->config[function] : NULL; + if ((c != NULL) && (c->state & CONFIG_LOCKED) && + (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { + u_char reg; + if (c->Present & PRESENT_PIN_REPLACE) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); + status->CardState |= + (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; + status->CardState |= + (reg & PRR_READY_STATUS) ? CS_EVENT_READY_CHANGE : 0; + status->CardState |= + (reg & PRR_BVD2_STATUS) ? CS_EVENT_BATTERY_LOW : 0; + status->CardState |= + (reg & PRR_BVD1_STATUS) ? CS_EVENT_BATTERY_DEAD : 0; + } else { + /* No PRR? Then assume we're always ready */ + status->CardState |= CS_EVENT_READY_CHANGE; + } + if (c->Present & PRESENT_EXT_STATUS) { + pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); + status->CardState |= + (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; + } + return CS_SUCCESS; + } + status->CardState |= + (val & SS_WRPROT) ? CS_EVENT_WRITE_PROTECT : 0; + status->CardState |= + (val & SS_BATDEAD) ? CS_EVENT_BATTERY_DEAD : 0; + status->CardState |= + (val & SS_BATWARN) ? CS_EVENT_BATTERY_LOW : 0; + status->CardState |= + (val & SS_READY) ? CS_EVENT_READY_CHANGE : 0; + return CS_SUCCESS; +} /* pccard_get_status */ + +int pcmcia_get_status(client_handle_t handle, cs_status_t *status) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + return pccard_get_status(s, handle->Function, status); +} +EXPORT_SYMBOL(pcmcia_get_status); + + + +/** pcmcia_get_mem_page + * + * Change the card address of an already open memory window. + */ +int pcmcia_get_mem_page(window_handle_t win, memreq_t *req) +{ + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + req->Page = 0; + req->CardOffset = win->ctl.card_start; + return CS_SUCCESS; +} /* pcmcia_get_mem_page */ +EXPORT_SYMBOL(pcmcia_get_mem_page); + + +int pcmcia_map_mem_page(window_handle_t win, memreq_t *req) +{ + struct pcmcia_socket *s; + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + if (req->Page != 0) + return CS_BAD_PAGE; + s = win->sock; + win->ctl.card_start = req->CardOffset; + if (s->ops->set_mem_map(s, &win->ctl) != 0) + return CS_BAD_OFFSET; + return CS_SUCCESS; +} /* pcmcia_map_mem_page */ +EXPORT_SYMBOL(pcmcia_map_mem_page); + + +/** pcmcia_modify_configuration + * + * Modify a locked socket configuration + */ +int pcmcia_modify_configuration(client_handle_t handle, + modconf_t *mod) +{ + struct pcmcia_socket *s; + config_t *c; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + c = CONFIG(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (!(c->state & CONFIG_LOCKED)) + return CS_CONFIGURATION_LOCKED; + + if (mod->Attributes & CONF_IRQ_CHANGE_VALID) { + if (mod->Attributes & CONF_ENABLE_IRQ) { + c->Attributes |= CONF_ENABLE_IRQ; + s->socket.io_irq = s->irq.AssignedIRQ; + } else { + c->Attributes &= ~CONF_ENABLE_IRQ; + s->socket.io_irq = 0; + } + s->ops->set_socket(s, &s->socket); + } + + if (mod->Attributes & CONF_VCC_CHANGE_VALID) + return CS_BAD_VCC; + + /* We only allow changing Vpp1 and Vpp2 to the same value */ + if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) && + (mod->Attributes & CONF_VPP2_CHANGE_VALID)) { + if (mod->Vpp1 != mod->Vpp2) + return CS_BAD_VPP; + c->Vpp1 = c->Vpp2 = s->socket.Vpp = mod->Vpp1; + if (s->ops->set_socket(s, &s->socket)) + return CS_BAD_VPP; + } else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) || + (mod->Attributes & CONF_VPP2_CHANGE_VALID)) + return CS_BAD_VPP; + + return CS_SUCCESS; +} /* modify_configuration */ +EXPORT_SYMBOL(pcmcia_modify_configuration); + + +int pcmcia_release_configuration(client_handle_t handle) +{ + pccard_io_map io = { 0, 0, 0, 0, 1 }; + struct pcmcia_socket *s; + int i; + + if (CHECK_HANDLE(handle) || + !(handle->state & CLIENT_CONFIG_LOCKED)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_CONFIG_LOCKED; + s = SOCKET(handle); + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_SUCCESS; +#endif + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (--(s->lock_count) == 0) { + s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */ + s->socket.Vpp = 0; + s->socket.io_irq = 0; + s->ops->set_socket(s, &s->socket); + } + if (c->state & CONFIG_IO_REQ) + for (i = 0; i < MAX_IO_WIN; i++) { + if (s->io[i].NumPorts == 0) + continue; + s->io[i].Config--; + if (s->io[i].Config != 0) + continue; + io.map = i; + s->ops->set_io_map(s, &io); + } + c->state &= ~CONFIG_LOCKED; + } + + return CS_SUCCESS; +} /* pcmcia_release_configuration */ +EXPORT_SYMBOL(pcmcia_release_configuration); + + +/** pcmcia_release_io + * + * Release_io() releases the I/O ranges allocated by a client. This + * may be invoked some time after a card ejection has already dumped + * the actual socket configuration, so if the client is "stale", we + * don't bother checking the port ranges against the current socket + * values. + */ +int pcmcia_release_io(client_handle_t handle, io_req_t *req) +{ + struct pcmcia_socket *s; + + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IO_REQ; + s = SOCKET(handle); + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_SUCCESS; +#endif + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if ((c->io.BasePort1 != req->BasePort1) || + (c->io.NumPorts1 != req->NumPorts1) || + (c->io.BasePort2 != req->BasePort2) || + (c->io.NumPorts2 != req->NumPorts2)) + return CS_BAD_ARGS; + c->state &= ~CONFIG_IO_REQ; + } + + release_io_space(s, req->BasePort1, req->NumPorts1); + if (req->NumPorts2) + release_io_space(s, req->BasePort2, req->NumPorts2); + + return CS_SUCCESS; +} /* pcmcia_release_io */ +EXPORT_SYMBOL(pcmcia_release_io); + + +int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) +{ + struct pcmcia_socket *s; + if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ)) + return CS_BAD_HANDLE; + handle->state &= ~CLIENT_IRQ_REQ; + s = SOCKET(handle); + + if (!(handle->state & CLIENT_STALE)) { + config_t *c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->irq.Attributes != req->Attributes) + return CS_BAD_ATTRIBUTE; + if (s->irq.AssignedIRQ != req->AssignedIRQ) + return CS_BAD_IRQ; + if (--s->irq.Config == 0) { + c->state &= ~CONFIG_IRQ_REQ; + s->irq.AssignedIRQ = 0; + } + } + + if (req->Attributes & IRQ_HANDLE_PRESENT) { + free_irq(req->AssignedIRQ, req->Instance); + } + +#ifdef CONFIG_PCMCIA_PROBE + pcmcia_used_irq[req->AssignedIRQ]--; +#endif + + return CS_SUCCESS; +} /* pcmcia_release_irq */ +EXPORT_SYMBOL(pcmcia_release_irq); + + +int pcmcia_release_window(window_handle_t win) +{ + struct pcmcia_socket *s; + + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) + return CS_BAD_HANDLE; + s = win->sock; + if (!(win->handle->state & CLIENT_WIN_REQ(win->index))) + return CS_BAD_HANDLE; + + /* Shut down memory window */ + win->ctl.flags &= ~MAP_ACTIVE; + s->ops->set_mem_map(s, &win->ctl); + s->state &= ~SOCKET_WIN_REQ(win->index); + + /* Release system memory */ + if (win->ctl.res) { + release_resource(win->ctl.res); + kfree(win->ctl.res); + win->ctl.res = NULL; + } + win->handle->state &= ~CLIENT_WIN_REQ(win->index); + + win->magic = 0; + + return CS_SUCCESS; +} /* pcmcia_release_window */ +EXPORT_SYMBOL(pcmcia_release_window); + + +int pcmcia_request_configuration(client_handle_t handle, + config_req_t *req) +{ + int i; + u_int base; + struct pcmcia_socket *s; + config_t *c; + pccard_io_map iomap; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + +#ifdef CONFIG_CARDBUS + if (handle->state & CLIENT_CARDBUS) + return CS_UNSUPPORTED_MODE; +#endif + + if (req->IntType & INT_CARDBUS) + return CS_UNSUPPORTED_MODE; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + + /* Do power control. We don't allow changes in Vcc. */ + if (s->socket.Vcc != req->Vcc) + return CS_BAD_VCC; + if (req->Vpp1 != req->Vpp2) + return CS_BAD_VPP; + s->socket.Vpp = req->Vpp1; + if (s->ops->set_socket(s, &s->socket)) + return CS_BAD_VPP; + + c->Vcc = req->Vcc; c->Vpp1 = c->Vpp2 = req->Vpp1; + + /* Pick memory or I/O card, DMA mode, interrupt */ + c->IntType = req->IntType; + c->Attributes = req->Attributes; + if (req->IntType & INT_MEMORY_AND_IO) + s->socket.flags |= SS_IOCARD; + if (req->IntType & INT_ZOOMED_VIDEO) + s->socket.flags |= SS_ZVCARD | SS_IOCARD; + if (req->Attributes & CONF_ENABLE_DMA) + s->socket.flags |= SS_DMA_MODE; + if (req->Attributes & CONF_ENABLE_SPKR) + s->socket.flags |= SS_SPKR_ENA; + if (req->Attributes & CONF_ENABLE_IRQ) + s->socket.io_irq = s->irq.AssignedIRQ; + else + s->socket.io_irq = 0; + s->ops->set_socket(s, &s->socket); + s->lock_count++; + + /* Set up CIS configuration registers */ + base = c->ConfigBase = req->ConfigBase; + c->Present = c->CardValues = req->Present; + if (req->Present & PRESENT_COPY) { + c->Copy = req->Copy; + pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy); + } + if (req->Present & PRESENT_OPTION) { + if (s->functions == 1) { + c->Option = req->ConfigIndex & COR_CONFIG_MASK; + } else { + c->Option = req->ConfigIndex & COR_MFC_CONFIG_MASK; + c->Option |= COR_FUNC_ENA|COR_IREQ_ENA; + if (req->Present & PRESENT_IOBASE_0) + c->Option |= COR_ADDR_DECODE; + } + if (c->state & CONFIG_IRQ_REQ) + if (!(c->irq.Attributes & IRQ_FORCED_PULSE)) + c->Option |= COR_LEVEL_REQ; + pcmcia_write_cis_mem(s, 1, (base + CISREG_COR)>>1, 1, &c->Option); + mdelay(40); + } + if (req->Present & PRESENT_STATUS) { + c->Status = req->Status; + pcmcia_write_cis_mem(s, 1, (base + CISREG_CCSR)>>1, 1, &c->Status); + } + if (req->Present & PRESENT_PIN_REPLACE) { + c->Pin = req->Pin; + pcmcia_write_cis_mem(s, 1, (base + CISREG_PRR)>>1, 1, &c->Pin); + } + if (req->Present & PRESENT_EXT_STATUS) { + c->ExtStatus = req->ExtStatus; + pcmcia_write_cis_mem(s, 1, (base + CISREG_ESR)>>1, 1, &c->ExtStatus); + } + if (req->Present & PRESENT_IOBASE_0) { + u_char b = c->io.BasePort1 & 0xff; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_0)>>1, 1, &b); + b = (c->io.BasePort1 >> 8) & 0xff; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOBASE_1)>>1, 1, &b); + } + if (req->Present & PRESENT_IOSIZE) { + u_char b = c->io.NumPorts1 + c->io.NumPorts2 - 1; + pcmcia_write_cis_mem(s, 1, (base + CISREG_IOSIZE)>>1, 1, &b); + } + + /* Configure I/O windows */ + if (c->state & CONFIG_IO_REQ) { + iomap.speed = io_speed; + for (i = 0; i < MAX_IO_WIN; i++) + if (s->io[i].NumPorts != 0) { + iomap.map = i; + iomap.flags = MAP_ACTIVE; + switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) { + case IO_DATA_PATH_WIDTH_16: + iomap.flags |= MAP_16BIT; break; + case IO_DATA_PATH_WIDTH_AUTO: + iomap.flags |= MAP_AUTOSZ; break; + default: + break; + } + iomap.start = s->io[i].BasePort; + iomap.stop = iomap.start + s->io[i].NumPorts - 1; + s->ops->set_io_map(s, &iomap); + s->io[i].Config++; + } + } + + c->state |= CONFIG_LOCKED; + handle->state |= CLIENT_CONFIG_LOCKED; + return CS_SUCCESS; +} /* pcmcia_request_configuration */ +EXPORT_SYMBOL(pcmcia_request_configuration); + + +/** pcmcia_request_io + * + * Request_io() reserves ranges of port addresses for a socket. + * I have not implemented range sharing or alias addressing. + */ +int pcmcia_request_io(client_handle_t handle, io_req_t *req) +{ + struct pcmcia_socket *s; + config_t *c; + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + + if (handle->state & CLIENT_CARDBUS) { +#ifdef CONFIG_CARDBUS + handle->state |= CLIENT_IO_REQ; + return CS_SUCCESS; +#else + return CS_UNSUPPORTED_FUNCTION; +#endif + } + + if (!req) + return CS_UNSUPPORTED_MODE; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->state & CONFIG_IO_REQ) + return CS_IN_USE; + if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) + return CS_BAD_ATTRIBUTE; + if ((req->NumPorts2 > 0) && + (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) + return CS_BAD_ATTRIBUTE; + + if (alloc_io_space(s, req->Attributes1, &req->BasePort1, + req->NumPorts1, req->IOAddrLines)) + return CS_IN_USE; + + if (req->NumPorts2) { + if (alloc_io_space(s, req->Attributes2, &req->BasePort2, + req->NumPorts2, req->IOAddrLines)) { + release_io_space(s, req->BasePort1, req->NumPorts1); + return CS_IN_USE; + } + } + + c->io = *req; + c->state |= CONFIG_IO_REQ; + handle->state |= CLIENT_IO_REQ; + return CS_SUCCESS; +} /* pcmcia_request_io */ +EXPORT_SYMBOL(pcmcia_request_io); + + +/** pcmcia_request_irq + * + * Request_irq() reserves an irq for this client. + * + * Also, since Linux only reserves irq's when they are actually + * hooked, we don't guarantee that an irq will still be available + * when the configuration is locked. Now that I think about it, + * there might be a way to fix this using a dummy handler. + */ + +#ifdef CONFIG_PCMCIA_PROBE +static irqreturn_t test_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + return IRQ_NONE; +} +#endif + +int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) +{ + struct pcmcia_socket *s; + config_t *c; + int ret = CS_IN_USE, irq = 0; + struct pcmcia_device *p_dev = handle_to_pdev(handle); + + if (CHECK_HANDLE(handle)) + return CS_BAD_HANDLE; + s = SOCKET(handle); + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + c = CONFIG(handle); + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->state & CONFIG_IRQ_REQ) + return CS_IN_USE; + +#ifdef CONFIG_PCMCIA_PROBE + if (s->irq.AssignedIRQ != 0) { + /* If the interrupt is already assigned, it must be the same */ + irq = s->irq.AssignedIRQ; + } else { + int try; + u32 mask = s->irq_mask; + void *data = NULL; + + for (try = 0; try < 64; try++) { + irq = try % 32; + + /* marked as available by driver, and not blocked by userspace? */ + if (!((mask >> irq) & 1)) + continue; + + /* avoid an IRQ which is already used by a PCMCIA card */ + if ((try < 32) && pcmcia_used_irq[irq]) + continue; + + /* register the correct driver, if possible, of check whether + * registering a dummy handle works, i.e. if the IRQ isn't + * marked as used by the kernel resource management core */ + ret = request_irq(irq, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Handler : test_action, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, + (req->Attributes & IRQ_HANDLE_PRESENT) ? req->Instance : data); + if (!ret) { + if (!(req->Attributes & IRQ_HANDLE_PRESENT)) + free_irq(irq, data); + break; + } + } + } +#endif + if (ret) { + if (!s->pci_irq) + return ret; + irq = s->pci_irq; + } + + if (ret && req->Attributes & IRQ_HANDLE_PRESENT) { + if (request_irq(irq, req->Handler, + ((req->Attributes & IRQ_TYPE_DYNAMIC_SHARING) || + (s->functions > 1) || + (irq == s->pci_irq)) ? SA_SHIRQ : 0, + p_dev->dev.bus_id, req->Instance)) + return CS_IN_USE; + } + + c->irq.Attributes = req->Attributes; + s->irq.AssignedIRQ = req->AssignedIRQ = irq; + s->irq.Config++; + + c->state |= CONFIG_IRQ_REQ; + handle->state |= CLIENT_IRQ_REQ; + +#ifdef CONFIG_PCMCIA_PROBE + pcmcia_used_irq[irq]++; +#endif + + return CS_SUCCESS; +} /* pcmcia_request_irq */ +EXPORT_SYMBOL(pcmcia_request_irq); + + +/** pcmcia_request_window + * + * Request_window() establishes a mapping between card memory space + * and system memory space. + */ +int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) +{ + struct pcmcia_socket *s; + window_t *win; + u_long align; + int w; + + if (CHECK_HANDLE(*handle)) + return CS_BAD_HANDLE; + s = (*handle)->Socket; + if (!(s->state & SOCKET_PRESENT)) + return CS_NO_CARD; + if (req->Attributes & (WIN_PAGED | WIN_SHARED)) + return CS_BAD_ATTRIBUTE; + + /* Window size defaults to smallest available */ + if (req->Size == 0) + req->Size = s->map_size; + align = (((s->features & SS_CAP_MEM_ALIGN) || + (req->Attributes & WIN_STRICT_ALIGN)) ? + req->Size : s->map_size); + if (req->Size & (s->map_size-1)) + return CS_BAD_SIZE; + if ((req->Base && (s->features & SS_CAP_STATIC_MAP)) || + (req->Base & (align-1))) + return CS_BAD_BASE; + if (req->Base) + align = 0; + + /* Allocate system memory window */ + for (w = 0; w < MAX_WIN; w++) + if (!(s->state & SOCKET_WIN_REQ(w))) break; + if (w == MAX_WIN) + return CS_OUT_OF_RESOURCE; + + win = &s->win[w]; + win->magic = WINDOW_MAGIC; + win->index = w; + win->handle = *handle; + win->sock = s; + + if (!(s->features & SS_CAP_STATIC_MAP)) { + win->ctl.res = pcmcia_find_mem_region(req->Base, req->Size, align, + (req->Attributes & WIN_MAP_BELOW_1MB), s); + if (!win->ctl.res) + return CS_IN_USE; + } + (*handle)->state |= CLIENT_WIN_REQ(w); + + /* Configure the socket controller */ + win->ctl.map = w+1; + win->ctl.flags = 0; + win->ctl.speed = req->AccessSpeed; + if (req->Attributes & WIN_MEMORY_TYPE) + win->ctl.flags |= MAP_ATTRIB; + if (req->Attributes & WIN_ENABLE) + win->ctl.flags |= MAP_ACTIVE; + if (req->Attributes & WIN_DATA_WIDTH_16) + win->ctl.flags |= MAP_16BIT; + if (req->Attributes & WIN_USE_WAIT) + win->ctl.flags |= MAP_USE_WAIT; + win->ctl.card_start = 0; + if (s->ops->set_mem_map(s, &win->ctl) != 0) + return CS_BAD_ARGS; + s->state |= SOCKET_WIN_REQ(w); + + /* Return window handle */ + if (s->features & SS_CAP_STATIC_MAP) { + req->Base = win->ctl.static_start; + } else { + req->Base = win->ctl.res->start; + } + *wh = win; + + return CS_SUCCESS; +} /* pcmcia_request_window */ +EXPORT_SYMBOL(pcmcia_request_window); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index b6843f8d300..0668384ebc8 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -72,7 +72,7 @@ int pcmcia_adjust_resource_info(adjust_t *adj) /* you can't use the old interface if the new * one was used before */ spin_lock_irqsave(&s->lock, flags); - if ((s->resource_setup_done) && + if ((s->resource_setup_new) && !(s->resource_setup_old)) { spin_unlock_irqrestore(&s->lock, flags); continue; @@ -105,29 +105,32 @@ void pcmcia_validate_mem(struct pcmcia_socket *s) } EXPORT_SYMBOL(pcmcia_validate_mem); -int adjust_io_region(struct resource *res, unsigned long r_start, +int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start, unsigned long r_end, struct pcmcia_socket *s) { if (s->resource_ops->adjust_io_region) return s->resource_ops->adjust_io_region(res, r_start, r_end, s); return -ENOMEM; } +EXPORT_SYMBOL(pcmcia_adjust_io_region); -struct resource *find_io_region(unsigned long base, int num, +struct resource *pcmcia_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { if (s->resource_ops->find_io) return s->resource_ops->find_io(base, num, align, s); return NULL; } +EXPORT_SYMBOL(pcmcia_find_io_region); -struct resource *find_mem_region(u_long base, u_long num, u_long align, +struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align, int low, struct pcmcia_socket *s) { if (s->resource_ops->find_mem) return s->resource_ops->find_mem(base, num, align, low, s); return NULL; } +EXPORT_SYMBOL(pcmcia_find_mem_region); void release_resource_db(struct pcmcia_socket *s) { diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5876bab7c14..c42455d20eb 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -372,6 +372,9 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) base, base+num-1); bad = fail = 0; step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); + /* don't allow too large steps */ + if (step > 0x800000) + step = 0x800000; /* cis_readable wants to map 2x map_size */ if (step < 2 * s->map_size) step = 2 * s->map_size; @@ -465,8 +468,7 @@ static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { mm = *m; - if (do_mem_probe(mm.base, mm.num, s)) - break; + do_mem_probe(mm.base, mm.num, s); } } @@ -601,7 +603,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ======================================================================*/ -struct resource *nonstatic_find_io_region(unsigned long base, int num, +static struct resource *nonstatic_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); @@ -635,8 +637,8 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, return res; } -struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, - int low, struct pcmcia_socket *s) +static struct resource * nonstatic_find_mem_region(u_long base, u_long num, + u_long align, int low, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id); struct socket_data *s_data = s->resource_data; @@ -683,27 +685,23 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig } -static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { - u_long base, num; struct socket_data *data = s->resource_data; - int ret; - - base = adj->resource.memory.Base; - num = adj->resource.memory.Size; - if ((num == 0) || (base+num-1 < base)) - return CS_BAD_SIZE; + unsigned long size = end - start + 1; + int ret = 0; - ret = CS_SUCCESS; + if (end <= start) + return -EINVAL; down(&rsrc_sem); - switch (adj->Action) { + switch (action) { case ADD_MANAGED_RESOURCE: - ret = add_interval(&data->mem_db, base, num); + ret = add_interval(&data->mem_db, start, size); break; case REMOVE_MANAGED_RESOURCE: - ret = sub_interval(&data->mem_db, base, num); - if (ret == CS_SUCCESS) { + ret = sub_interval(&data->mem_db, start, size); + if (!ret) { struct pcmcia_socket *socket; down_read(&pcmcia_socket_list_rwsem); list_for_each_entry(socket, &pcmcia_socket_list, socket_list) @@ -712,7 +710,7 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) } break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; } up(&rsrc_sem); @@ -720,36 +718,35 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) } -static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) +static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) { struct socket_data *data = s->resource_data; - kio_addr_t base, num; - int ret = CS_SUCCESS; + unsigned long size = end - start + 1; + int ret = 0; - base = adj->resource.io.BasePort; - num = adj->resource.io.NumPorts; - if ((base < 0) || (base > 0xffff)) - return CS_BAD_BASE; - if ((num <= 0) || (base+num > 0x10000) || (base+num <= base)) - return CS_BAD_SIZE; + if (end <= start) + return -EINVAL; + + if (end > IO_SPACE_LIMIT) + return -EINVAL; down(&rsrc_sem); - switch (adj->Action) { + switch (action) { case ADD_MANAGED_RESOURCE: - if (add_interval(&data->io_db, base, num) != 0) { - ret = CS_IN_USE; + if (add_interval(&data->io_db, start, size) != 0) { + ret = -EBUSY; break; } #ifdef CONFIG_PCMCIA_PROBE if (probe_io) - do_io_probe(s, base, num); + do_io_probe(s, start, size); #endif break; case REMOVE_MANAGED_RESOURCE: - sub_interval(&data->io_db, base, num); + sub_interval(&data->io_db, start, size); break; default: - ret = CS_UNSUPPORTED_FUNCTION; + ret = -EINVAL; break; } up(&rsrc_sem); @@ -760,15 +757,82 @@ static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) { + unsigned long end; + switch (adj->Resource) { case RES_MEMORY_RANGE: - return adjust_memory(s, adj); + end = adj->resource.memory.Base + adj->resource.memory.Size - 1; + return adjust_memory(s, adj->Action, adj->resource.memory.Base, end); case RES_IO_RANGE: - return adjust_io(s, adj); + end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; + return adjust_io(s, adj->Action, adj->resource.io.BasePort, end); } return CS_UNSUPPORTED_FUNCTION; } +#ifdef CONFIG_PCI +static int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + struct resource *res; + int i, done = 0; + + if (!s->cb_dev || !s->cb_dev->bus) + return -ENODEV; + +#if defined(CONFIG_X86) || defined(CONFIG_X86_64) + /* If this is the root bus, the risk of hitting + * some strange system devices which aren't protected + * by either ACPI resource tables or properly requested + * resources is too big. Therefore, don't do auto-adding + * of resources at the moment. + */ + if (s->cb_dev->bus->number == 0) + return -EINVAL; +#endif + + for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) { + res = s->cb_dev->bus->resource[i]; + if (!res) + continue; + + if (res->flags & IORESOURCE_IO) { + if (res == &ioport_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge I/O window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_IO; + + } + + if (res->flags & IORESOURCE_MEM) { + if (res == &iomem_resource) + continue; + printk(KERN_INFO "pcmcia: parent PCI bridge Memory window: 0x%lx - 0x%lx\n", + res->start, res->end); + if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) + done |= IORESOURCE_MEM; + } + } + + /* if we got at least one of IO, and one of MEM, we can be glad and + * activate the PCMCIA subsystem */ + if (done & (IORESOURCE_MEM | IORESOURCE_IO)) + s->resource_setup_done = 1; + + return 0; +} + +#else + +static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) +{ + return -ENODEV; +} + +#endif + + static int nonstatic_init(struct pcmcia_socket *s) { struct socket_data *data; @@ -783,6 +847,8 @@ static int nonstatic_init(struct pcmcia_socket *s) s->resource_data = (void *) data; + nonstatic_autoadd_resources(s); + return 0; } @@ -845,17 +911,16 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } @@ -863,12 +928,9 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size if (end_addr <= start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_IO_RANGE; - adj.resource.io.BasePort = start_addr; - adj.resource.io.NumPorts = end_addr - start_addr + 1; - - ret = adjust_io(s, &adj); + ret = adjust_io(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } @@ -901,17 +963,16 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz { struct pcmcia_socket *s = class_get_devdata(class_dev); unsigned long start_addr, end_addr; - unsigned int add = 1; - adjust_t adj; + unsigned int add = ADD_MANAGED_RESOURCE; ssize_t ret = 0; ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); if (ret != 2) { ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); - add = 0; + add = REMOVE_MANAGED_RESOURCE; if (ret != 2) { ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); - add = 1; + add = ADD_MANAGED_RESOURCE; if (ret != 2) return -EINVAL; } @@ -919,12 +980,9 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz if (end_addr <= start_addr) return -EINVAL; - adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; - adj.Resource = RES_MEMORY_RANGE; - adj.resource.memory.Base = start_addr; - adj.resource.memory.Size = end_addr - start_addr + 1; - - ret = adjust_memory(s, &adj); + ret = adjust_memory(s, add, start_addr, end_addr); + if (!ret) + s->resource_setup_new = 1; return ret ? ret : count; } diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 8eed0393821..fcef54c1c2d 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -163,28 +163,164 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf, return -EINVAL; spin_lock_irqsave(&s->lock, flags); - if (!s->resource_setup_done) { + if (!s->resource_setup_done) s->resource_setup_done = 1; - spin_unlock_irqrestore(&s->lock, flags); + spin_unlock_irqrestore(&s->lock, flags); + + down(&s->skt_sem); + if ((s->callback) && + (s->state & SOCKET_PRESENT) && + !(s->state & SOCKET_CARDBUS)) { + if (try_module_get(s->callback->owner)) { + s->callback->requery(s); + module_put(s->callback->owner); + } + } + up(&s->skt_sem); + + return count; +} +static CLASS_DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); + + +static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count) +{ + tuple_t tuple; + int status, i; + loff_t pointer = 0; + ssize_t ret = 0; + u_char *tuplebuffer; + u_char *tempbuffer; + + tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL); + if (!tuplebuffer) + return -ENOMEM; + + tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL); + if (!tempbuffer) { + ret = -ENOMEM; + goto free_tuple; + } + + memset(&tuple, 0, sizeof(tuple_t)); + + tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + tuple.TupleOffset = 0; + + status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple); + while (!status) { + tuple.TupleData = tuplebuffer; + tuple.TupleDataMax = 255; + memset(tuplebuffer, 0, sizeof(u_char) * 255); + status = pccard_get_tuple_data(s, &tuple); + if (status) + break; + + if (off < (pointer + 2 + tuple.TupleDataLen)) { + tempbuffer[0] = tuple.TupleCode & 0xff; + tempbuffer[1] = tuple.TupleLink & 0xff; + for (i = 0; i < tuple.TupleDataLen; i++) + tempbuffer[i + 2] = tuplebuffer[i] & 0xff; + + for (i = 0; i < (2 + tuple.TupleDataLen); i++) { + if (((i + pointer) >= off) && + (i + pointer) < (off + count)) { + buf[ret] = tempbuffer[i]; + ret++; + } + } + } + + pointer += 2 + tuple.TupleDataLen; + + if (pointer >= (off + count)) + break; + + if (tuple.TupleCode == CISTPL_END) + break; + status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple); + } + + kfree(tempbuffer); + free_tuple: + kfree(tuplebuffer); + + return (ret); +} + +static ssize_t pccard_show_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + unsigned int size = 0x200; + + if (off >= size) + count = 0; + else { + struct pcmcia_socket *s; + cisinfo_t cisinfo; + + if (off + count > size) + count = size - off; + + s = to_socket(container_of(kobj, struct class_device, kobj)); + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + if (pccard_validate_cis(s, BIND_FN_ALL, &cisinfo)) + return -EIO; + if (!cisinfo.Chains) + return -ENODATA; + + count = pccard_extract_cis(s, buf, off, count); + } + + return (count); +} + +static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +{ + struct pcmcia_socket *s = to_socket(container_of(kobj, struct class_device, kobj)); + cisdump_t *cis; + ssize_t ret = count; + + if (off) + return -EINVAL; + + if (count >= 0x200) + return -EINVAL; + + if (!(s->state & SOCKET_PRESENT)) + return -ENODEV; + + cis = kmalloc(sizeof(cisdump_t), GFP_KERNEL); + if (!cis) + return -ENOMEM; + memset(cis, 0, sizeof(cisdump_t)); + + cis->Length = count + 1; + memcpy(cis->Data, buf, count); + + if (pcmcia_replace_cis(s, cis)) + ret = -EIO; + + kfree(cis); + + if (!ret) { down(&s->skt_sem); - if ((s->callback) && - (s->state & SOCKET_PRESENT) && + if ((s->callback) && (s->state & SOCKET_PRESENT) && !(s->state & SOCKET_CARDBUS)) { if (try_module_get(s->callback->owner)) { - s->callback->resources_done(s); + s->callback->requery(s); module_put(s->callback->owner); } } up(&s->skt_sem); - - return count; } - spin_unlock_irqrestore(&s->lock, flags); - return count; + + return (ret); } -static CLASS_DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource); static struct class_device_attribute *pccard_socket_attributes[] = { @@ -199,6 +335,13 @@ static struct class_device_attribute *pccard_socket_attributes[] = { NULL, }; +static struct bin_attribute pccard_cis_attr = { + .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR, .owner = THIS_MODULE}, + .size = 0x200, + .read = pccard_show_cis, + .write = pccard_store_cis, +}; + static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev) { struct class_device_attribute **attr; @@ -209,6 +352,8 @@ static int __devinit pccard_sysfs_add_socket(struct class_device *class_dev) if (ret) break; } + if (!ret) + ret = sysfs_create_bin_file(&class_dev->kobj, &pccard_cis_attr); return ret; } @@ -217,6 +362,7 @@ static void __devexit pccard_sysfs_remove_socket(struct class_device *class_dev) { struct class_device_attribute **attr; + sysfs_remove_bin_file(&class_dev->kobj, &pccard_cis_attr); for (attr = pccard_socket_attributes; *attr; attr++) class_device_remove_file(class_dev, *attr); } diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index bee05362fd2..02b23abc2df 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -549,6 +549,11 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ unsigned offset; unsigned mask; + res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; + /* Already allocated? */ + if (res->parent) + return 0; + /* The granularity of the memory limit is 4kB, on IO it's 4 bytes */ mask = ~0xfff; if (type & IORESOURCE_IO) @@ -556,7 +561,6 @@ static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned typ offset = 0x1c + 8*nr; bus = socket->dev->subordinate; - res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; res->name = bus->name; res->flags = type; res->start = 0; diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index e60b4c0a842..f1f6bf596dc 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -318,6 +318,16 @@ static int aha152x_event(event_t event, int priority, return 0; } +static struct pcmcia_device_id aha152x_ids[] = { + PCMCIA_DEVICE_PROD_ID123("New Media", "SCSI", "Bus Toaster", 0xcdf7e4cc, 0x35f26476, 0xa8851d6e), + PCMCIA_DEVICE_PROD_ID123("NOTEWORTHY", "SCSI", "Bus Toaster", 0xad89c6e8, 0x35f26476, 0xa8851d6e), + PCMCIA_DEVICE_PROD_ID12("Adaptec, Inc.", "APA-1460 SCSI Host Adapter", 0x24ba9738, 0x3a3c3d20), + PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Multimedia Sound/SCSI", 0x085a850b, 0x80a6535c), + PCMCIA_DEVICE_PROD_ID12("NOTEWORTHY", "NWCOMB02 SCSI/AUDIO COMBO CARD", 0xad89c6e8, 0x5f9a615b), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, aha152x_ids); + static struct pcmcia_driver aha152x_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -325,6 +335,7 @@ static struct pcmcia_driver aha152x_cs_driver = { }, .attach = aha152x_attach, .detach = aha152x_detach, + .id_table = aha152x_ids, }; static int __init init_aha152x_cs(void) diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 3df7bc72e35..853e6ee9b71 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -299,6 +299,15 @@ static int fdomain_event(event_t event, int priority, return 0; } /* fdomain_event */ + +static struct pcmcia_device_id fdomain_ids[] = { + PCMCIA_DEVICE_PROD_ID12("IBM Corp.", "SCSI PCMCIA Card", 0xe3736c88, 0x859cad20), + PCMCIA_DEVICE_PROD_ID1("SCSI PCMCIA Adapter Card", 0x8dacb57e), + PCMCIA_DEVICE_PROD_ID12(" SIMPLE TECHNOLOGY Corporation", "SCSI PCMCIA Credit Card Controller", 0x182bdafe, 0xc80d106f), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, fdomain_ids); + static struct pcmcia_driver fdomain_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -306,6 +315,7 @@ static struct pcmcia_driver fdomain_cs_driver = { }, .attach = fdomain_attach, .detach = fdomain_detach, + .id_table = fdomain_ids, }; static int __init init_fdomain_cs(void) diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 3dddb323e71..91b3f28e7a1 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -2125,6 +2125,18 @@ static int nsp_cs_event(event_t event, * module entry point *====================================================================*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68)) +static struct pcmcia_device_id nsp_cs_ids[] = { + PCMCIA_DEVICE_PROD_ID123("IO DATA", "CBSC16 ", "1", 0x547e66dc, 0x0d63a3fd, 0x51de003a), + PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-001", "1", 0x534c02bc, 0x52008408, 0x51de003a), + PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-002", "1", 0x534c02bc, 0xcb09d5b2, 0x51de003a), + PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-003", "1", 0x534c02bc, 0xbc0ee524, 0x51de003a), + PCMCIA_DEVICE_PROD_ID123("KME ", "SCSI-CARD-004", "1", 0x534c02bc, 0x226a7087, 0x51de003a), + PCMCIA_DEVICE_PROD_ID123("WBT", "NinjaSCSI-3", "R1.0", 0xc7ba805f, 0xfdc7c97d, 0x6973710e), + PCMCIA_DEVICE_PROD_ID123("WORKBIT", "UltraNinja-16", "1", 0x28191418, 0xb70f4b09, 0x51de003a), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, nsp_cs_ids); + static struct pcmcia_driver nsp_driver = { .owner = THIS_MODULE, .drv = { @@ -2132,6 +2144,7 @@ static struct pcmcia_driver nsp_driver = { }, .attach = nsp_cs_attach, .detach = nsp_cs_detach, + .id_table = nsp_cs_ids, }; #endif diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index a0175f5d11c..0dcf41102ab 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -395,6 +395,27 @@ static int qlogic_event(event_t event, int priority, event_callback_args_t * arg return 0; } /* qlogic_event */ +static struct pcmcia_device_id qlogic_ids[] = { + PCMCIA_DEVICE_PROD_ID12("EIger Labs", "PCMCIA-to-SCSI Adapter", 0x88395fa7, 0x33b7a5e6), + PCMCIA_DEVICE_PROD_ID12("EPSON", "SCSI-2 PC Card SC200", 0xd361772f, 0x299d1751), + PCMCIA_DEVICE_PROD_ID12("MACNICA", "MIRACLE SCSI-II mPS110", 0x20841b68, 0xab3c3b6d), + PCMCIA_DEVICE_PROD_ID12("MIDORI ELECTRONICS ", "CN-SC43", 0x6534382a, 0xd67eee79), + PCMCIA_DEVICE_PROD_ID12("NEC", "PC-9801N-J03R", 0x18df0ba0, 0x24662e8a), + PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC003", 0x82375a27, 0xf68e5bf7), + PCMCIA_DEVICE_PROD_ID12("KME ", "KXLC004", 0x82375a27, 0x68eace54), + PCMCIA_DEVICE_PROD_ID12("KME", "KXLC101", 0x3faee676, 0x194250ec), + PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05", 0xd77b2930, 0xa85b2735), + PCMCIA_DEVICE_PROD_ID12("QLOGIC CORPORATION", "pc05 rev 1.10", 0xd77b2930, 0x70f8b5f8), + PCMCIA_DEVICE_PROD_ID123("KME", "KXLC002", "00", 0x3faee676, 0x81896b61, 0xf99f065f), + PCMCIA_DEVICE_PROD_ID12("RATOC System Inc.", "SCSI2 CARD 37", 0x85c10e17, 0x1a2640c1), + PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200A PC CARD SCSI", 0xb4585a1a, 0xa6f06ebe), + PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "SCSC200B PC CARD SCSI-10", 0xb4585a1a, 0x0a88dea0), + /* these conflict with other cards! */ + /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */ + /* PCMCIA_DEVICE_PROD_ID123("MACNICA", "MIRACLE SCSI", "mPS100", 0x20841b68, 0xf8dedaeb, 0x89f7fafb), */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, qlogic_ids); static struct pcmcia_driver qlogic_cs_driver = { .owner = THIS_MODULE, @@ -403,6 +424,7 @@ static struct pcmcia_driver qlogic_cs_driver = { }, .attach = qlogic_attach, .detach = qlogic_detach, + .id_table = qlogic_ids, }; static int __init init_qlogic_cs(void) diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index 1667da9508b..7d4b16b6797 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -999,6 +999,14 @@ MODULE_AUTHOR("Bob Tracy <rct@frus.com>"); MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver"); MODULE_LICENSE("GPL"); +static struct pcmcia_device_id sym53c500_ids[] = { + PCMCIA_DEVICE_PROD_ID12("BASICS by New Media Corporation", "SCSI Sym53C500", 0x23c78a9d, 0x0099e7f7), + PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "SCSI Bus Toaster Sym53C500", 0x085a850b, 0x45432eb8), + PCMCIA_DEVICE_PROD_ID2("SCSI9000", 0x21648f44), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, sym53c500_ids); + static struct pcmcia_driver sym53c500_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -1006,6 +1014,7 @@ static struct pcmcia_driver sym53c500_cs_driver = { }, .attach = SYM53C500_attach, .detach = SYM53C500_detach, + .id_table = sym53c500_ids, }; static int __init diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 0d7b65f93e8..73a34b18866 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -772,6 +772,111 @@ serial_event(event_t event, int priority, event_callback_args_t * args) return 0; } +static struct pcmcia_device_id serial_ids[] = { + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0140, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0x3341), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0143, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM28", 0x2e3ee845, 0x0ea978ea), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM33", 0x2e3ee845, 0x80609023), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "CEM56", 0x2e3ee845, 0xa650c32a), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), + PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet ", 0x578ba6e7, 0x02d92d1e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Linksys", "EtherFast 10&100 + 56K PC Card (PCMLM56)", 0x0733cc81, 0xb3765033), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "LINKSYS", "PCMLM336", 0xf7cb0b07, 0x7a821b58), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Ositech", "Trumpcard", 0x0c2f80cd, 0x0573c29f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "ComboCard", 0xdcfe12d3, 0xcd8906cc), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet", 0x2e3ee845, 0xc0e778c2), + PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0104, 0x0070), + PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x016c, 0x0020), + PCMCIA_MFC_DEVICE_PROD_ID123(1, "APEX DATA", "MULTICARD", "ETHERNET-MODEM", 0x11c2da09, 0x7289dc5d, 0xaad95e1f), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away 28.8 PC Card ", 0xb569a6e5, 0x5bd4ff2c), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "Home and Away Credit Card Adapter", 0xb569a6e5, 0x4bdf15c3), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "IBM", "w95 Home and Away Credit Card ", 0xb569a6e5, 0xae911c15), + PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), + PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), + PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), + PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), + PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), + PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), + PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), + PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), + PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), + PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), + PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), + PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 28800 FAX/DATA MODEM", 0xa3a3062c, 0x8cbd7c76), + PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), + PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), + PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), + PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), + PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400", 0x816cc815, 0x23539b80), + PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), + PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), + PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), + PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "3CCFEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "3CXEM556.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "3CXEM556.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "MT5634ZLX.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "RS-COM-2P.cis"), + /* too generic */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0160, 0x0002), */ + /* PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0160, 0x0002), */ + PCMCIA_DEVICE_FUNC_ID(2), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, serial_ids); + static struct pcmcia_driver serial_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -779,6 +884,7 @@ static struct pcmcia_driver serial_cs_driver = { }, .attach = serial_attach, .detach = serial_detach, + .id_table = serial_ids, }; static int __init init_serial_cs(void) diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c index e1ef0d7ee8d..ce5ebfe4af2 100644 --- a/drivers/telephony/ixj_pcmcia.c +++ b/drivers/telephony/ixj_pcmcia.c @@ -295,6 +295,12 @@ static int ixj_event(event_t event, int priority, event_callback_args_t * args) return 0; } +static struct pcmcia_device_id ixj_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0x0257, 0x0600), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, ixj_ids); + static struct pcmcia_driver ixj_driver = { .owner = THIS_MODULE, .drv = { @@ -302,6 +308,7 @@ static struct pcmcia_driver ixj_driver = { }, .attach = ixj_attach, .detach = ixj_detach, + .id_table = ixj_ids, }; static int __init ixj_pcmcia_init(void) diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 6e173265095..269d8ef0145 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -68,13 +68,6 @@ static const char driver_name[DEV_NAME_LEN] = "sl811_cs"; static dev_link_t *dev_list = NULL; -static int irq_list[4] = { -1 }; -static int irq_list_count; - -module_param_array(irq_list, int, &irq_list_count, 0444); - -INT_MODULE_PARM(irq_mask, 0xdeb8); - typedef struct local_info_t { dev_link_t link; dev_node_t node; @@ -373,7 +366,7 @@ static dev_link_t *sl811_cs_attach(void) local_info_t *local; dev_link_t *link; client_reg_t client_reg; - int ret, i; + int ret; local = kmalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) @@ -385,11 +378,6 @@ static dev_link_t *sl811_cs_attach(void) /* Initialize */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; - if (irq_list[0] == -1) - link->irq.IRQInfo2 = irq_mask; - else - for (i = 0; i < irq_list_count; i++) - link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = NULL; link->conf.Attributes = 0; @@ -418,6 +406,12 @@ static dev_link_t *sl811_cs_attach(void) return link; } +static struct pcmcia_device_id sl811_ids[] = { + PCMCIA_DEVICE_MANF_CARD(0xc015, 0x0001), /* RATOC USB HOST CF+ Card */ + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, sl811_ids); + static struct pcmcia_driver sl811_cs_driver = { .owner = THIS_MODULE, .drv = { @@ -425,6 +419,7 @@ static struct pcmcia_driver sl811_cs_driver = { }, .attach = sl811_cs_attach, .detach = sl811_cs_detach, + .id_table = sl811_ids, }; /*====================================================================*/ |