diff options
Diffstat (limited to 'drivers/scsi/3w-9xxx.c')
-rw-r--r-- | drivers/scsi/3w-9xxx.c | 205 |
1 files changed, 79 insertions, 126 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index eb766c3af1c..efd9d8d3a89 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -4,7 +4,7 @@ Written By: Adam Radford <linuxraid@amcc.com> Modifications By: Tom Couch <linuxraid@amcc.com> - Copyright (C) 2004-2006 Applied Micro Circuits Corporation. + Copyright (C) 2004-2007 Applied Micro Circuits Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -69,6 +69,8 @@ 2.26.02.008 - Free irq handler in __twa_shutdown(). Serialize reset code. Add support for 9650SE controllers. + 2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails. + 2.26.02.010 - Add support for 9690SA controllers. */ #include <linux/module.h> @@ -92,7 +94,7 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.008" +#define TW_DRIVER_VERSION "2.26.02.010" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -124,11 +126,11 @@ static int twa_initconnection(TW_Device_Extension *tw_dev, int message_credits, unsigned short *fw_on_ctlr_branch, unsigned short *fw_on_ctlr_build, u32 *init_connect_result); -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); +static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length); static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds); static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds); static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal); -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); +static int twa_reset_device_extension(TW_Device_Extension *tw_dev); static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset); static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, char *cdb, int use_sg, TW_SG_Entry *sglistarg); static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id); @@ -683,7 +685,7 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int full_command_packet = &tw_ioctl->firmware_command; /* Load request id and sglist for both command types */ - twa_load_sgl(full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); + twa_load_sgl(tw_dev, full_command_packet, request_id, dma_handle, data_buffer_length_adjusted); memcpy(tw_dev->command_packet_virt[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command_Full)); @@ -700,10 +702,10 @@ static int twa_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */ printk(KERN_WARNING "3w-9xxx: scsi%d: WARNING: (0x%02X:0x%04X): Character ioctl (0x%x) timed out, resetting card.\n", - tw_dev->host->host_no, TW_DRIVER, 0xc, + tw_dev->host->host_no, TW_DRIVER, 0x37, cmd); retval = TW_IOCTL_ERROR_OS_EIO; - twa_reset_device_extension(tw_dev, 1); + twa_reset_device_extension(tw_dev); goto out3; } @@ -890,7 +892,9 @@ static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value) } if (status_reg_value & TW_STATUS_QUEUE_ERROR) { - if ((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) || (!test_bit(TW_IN_RESET, &tw_dev->flags))) + if (((tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9650SE) && + (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9690SA)) || + (!test_bit(TW_IN_RESET, &tw_dev->flags))) TW_PRINTK(tw_dev->host, TW_DRIVER, 0xe, "Controller Queue Error: clearing"); writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); } @@ -935,8 +939,7 @@ static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev) unsigned long before; int retval = 1; - if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) || - (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE)) { + if (tw_dev->tw_pci_dev->device != PCI_DEVICE_ID_3WARE_9000) { before = jiffies; while ((response_que_value & TW_9550SX_DRAIN_COMPLETED) != TW_9550SX_DRAIN_COMPLETED) { response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev)); @@ -1160,13 +1163,12 @@ static int twa_initialize_device_extension(TW_Device_Extension *tw_dev) } /* Allocate event info space */ - tw_dev->event_queue[0] = kmalloc(sizeof(TW_Event) * TW_Q_LENGTH, GFP_KERNEL); + tw_dev->event_queue[0] = kcalloc(TW_Q_LENGTH, sizeof(TW_Event), GFP_KERNEL); if (!tw_dev->event_queue[0]) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x18, "Event info memory allocation failed"); goto out; } - memset(tw_dev->event_queue[0], 0, sizeof(TW_Event) * TW_Q_LENGTH); for (i = 0; i < TW_Q_LENGTH; i++) { tw_dev->event_queue[i] = (TW_Event *)((unsigned char *)tw_dev->event_queue[0] + (i * sizeof(TW_Event))); @@ -1196,7 +1198,6 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) u32 status_reg_value; TW_Response_Queue response_que; TW_Command_Full *full_command_packet; - TW_Command *command_packet; TW_Device_Extension *tw_dev = (TW_Device_Extension *)dev_instance; int handled = 0; @@ -1274,7 +1275,6 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) request_id = TW_RESID_OUT(response_que.response_id); full_command_packet = tw_dev->command_packet_virt[request_id]; error = 0; - command_packet = &full_command_packet->command.oldcommand; /* Check for command packet errors */ if (full_command_packet->command.newcommand.status != 0) { if (tw_dev->srb[request_id] != 0) { @@ -1306,22 +1306,26 @@ static irqreturn_t twa_interrupt(int irq, void *dev_instance) wake_up(&tw_dev->ioctl_wqueue); } } else { + struct scsi_cmnd *cmd; + + cmd = tw_dev->srb[request_id]; + twa_scsiop_execute_scsi_complete(tw_dev, request_id); /* If no error command was a success */ if (error == 0) { - tw_dev->srb[request_id]->result = (DID_OK << 16); + cmd->result = (DID_OK << 16); } /* If error, command failed */ if (error == 1) { /* Ask for a host reset */ - tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + cmd->result = (DID_OK << 16) | (CHECK_CONDITION << 1); } /* Report residual bytes for single sgl */ - if ((tw_dev->srb[request_id]->use_sg <= 1) && (full_command_packet->command.newcommand.status == 0)) { - if (full_command_packet->command.newcommand.sg_list[0].length < tw_dev->srb[request_id]->request_bufflen) - tw_dev->srb[request_id]->resid = tw_dev->srb[request_id]->request_bufflen - full_command_packet->command.newcommand.sg_list[0].length; + if ((scsi_sg_count(cmd) <= 1) && (full_command_packet->command.newcommand.status == 0)) { + if (full_command_packet->command.newcommand.sg_list[0].length < scsi_bufflen(tw_dev->srb[request_id])) + scsi_set_resid(cmd, scsi_bufflen(cmd) - full_command_packet->command.newcommand.sg_list[0].length); } /* Now complete the io */ @@ -1349,11 +1353,15 @@ twa_interrupt_bail: } /* End twa_interrupt() */ /* This function will load the request id and various sgls for ioctls */ -static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) +static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) { TW_Command *oldcommand; TW_Command_Apache *newcommand; TW_SG_Entry *sgl; + unsigned int pae = 0; + + if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) + pae = 1; if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) { newcommand = &full_command_packet->command.newcommand; @@ -1369,12 +1377,14 @@ static void twa_load_sgl(TW_Command_Full *full_command_packet, int request_id, d if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) { /* Load the sg list */ - sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset)); + if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA) + sgl = (TW_SG_Entry *)((u32 *)oldcommand+oldcommand->size - (sizeof(TW_SG_Entry)/4) + pae); + else + sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset)); sgl->address = TW_CPU_TO_SGL(dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1); sgl->length = cpu_to_le32(length); - if ((sizeof(long) < 8) && (sizeof(dma_addr_t) > 4)) - oldcommand->size += 1; + oldcommand->size += pae; } } } /* End twa_load_sgl() */ @@ -1384,52 +1394,20 @@ static int twa_map_scsi_sg_data(TW_Device_Extension *tw_dev, int request_id) { int use_sg; struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - int retval = 0; - - if (cmd->use_sg == 0) - goto out; - - use_sg = pci_map_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - if (use_sg == 0) { + use_sg = scsi_dma_map(cmd); + if (!use_sg) + return 0; + else if (use_sg < 0) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list"); - goto out; + return 0; } cmd->SCp.phase = TW_PHASE_SGLIST; cmd->SCp.have_data_in = use_sg; - retval = use_sg; -out: - return retval; -} /* End twa_map_scsi_sg_data() */ -/* This function will perform a pci-dma map for a single buffer */ -static dma_addr_t twa_map_scsi_single_data(TW_Device_Extension *tw_dev, int request_id) -{ - dma_addr_t mapping; - struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - dma_addr_t retval = 0; - - if (cmd->request_bufflen == 0) { - retval = 0; - goto out; - } - - mapping = pci_map_single(pdev, cmd->request_buffer, cmd->request_bufflen, DMA_BIDIRECTIONAL); - - if (mapping == 0) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1d, "Failed to map page"); - goto out; - } - - cmd->SCp.phase = TW_PHASE_SINGLE; - cmd->SCp.have_data_in = mapping; - retval = mapping; -out: - return retval; -} /* End twa_map_scsi_single_data() */ + return use_sg; +} /* End twa_map_scsi_sg_data() */ /* This function will poll for a response interrupt of a request */ static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds) @@ -1535,7 +1513,8 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, command_que_value = tw_dev->command_packet_phys[request_id]; /* For 9650SE write low 4 bytes first */ - if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { + if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { command_que_value += TW_COMMAND_OFFSET; writel((u32)command_que_value, TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev)); } @@ -1566,7 +1545,8 @@ static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, TW_UNMASK_COMMAND_INTERRUPT(tw_dev); goto out; } else { - if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) { + if ((tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9690SA)) { /* Now write upper 4 bytes */ writel((u32)((u64)command_que_value >> 32), TW_COMMAND_QUEUE_REG_ADDR_LARGE(tw_dev) + 0x4); } else { @@ -1590,7 +1570,7 @@ out: } /* End twa_post_command_packet() */ /* This function will reset a device extension */ -static int twa_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) +static int twa_reset_device_extension(TW_Device_Extension *tw_dev) { int i = 0; int retval = 1; @@ -1748,7 +1728,7 @@ static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt) mutex_lock(&tw_dev->ioctl_lock); /* Now reset the card and some of the device extension data */ - if (twa_reset_device_extension(tw_dev, 0)) { + if (twa_reset_device_extension(tw_dev)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2b, "Controller reset failed during scsi host reset"); goto out; } @@ -1815,15 +1795,13 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, u32 num_sectors = 0x0; int i, sg_count; struct scsi_cmnd *srb = NULL; - struct scatterlist *sglist = NULL; - dma_addr_t buffaddr = 0x0; + struct scatterlist *sglist = NULL, *sg; int retval = 1; if (tw_dev->srb[request_id]) { - if (tw_dev->srb[request_id]->request_buffer) { - sglist = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; - } srb = tw_dev->srb[request_id]; + if (scsi_sglist(srb)) + sglist = scsi_sglist(srb); } /* Initialize command packet */ @@ -1856,32 +1834,12 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (!sglistarg) { /* Map sglist from scsi layer to cmd packet */ - if (tw_dev->srb[request_id]->use_sg == 0) { - if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) { - command_packet->sg_list[0].address = TW_CPU_TO_SGL(tw_dev->generic_buffer_phys[request_id]); - command_packet->sg_list[0].length = cpu_to_le32(TW_MIN_SGL_LENGTH); - if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) - memcpy(tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen); - } else { - buffaddr = twa_map_scsi_single_data(tw_dev, request_id); - if (buffaddr == 0) - goto out; - - command_packet->sg_list[0].address = TW_CPU_TO_SGL(buffaddr); - command_packet->sg_list[0].length = cpu_to_le32(tw_dev->srb[request_id]->request_bufflen); - } - command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), 1)); - - if (command_packet->sg_list[0].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2d, "Found unaligned address during execute scsi"); - goto out; - } - } - if (tw_dev->srb[request_id]->use_sg > 0) { - if ((tw_dev->srb[request_id]->use_sg == 1) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { - if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) { - struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + if (scsi_sg_count(srb)) { + if ((scsi_sg_count(srb) == 1) && + (scsi_bufflen(srb) < TW_MIN_SGL_LENGTH)) { + if (srb->sc_data_direction == DMA_TO_DEVICE || srb->sc_data_direction == DMA_BIDIRECTIONAL) { + struct scatterlist *sg = scsi_sglist(srb); char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; memcpy(tw_dev->generic_buffer_virt[request_id], buf, sg->length); kunmap_atomic(buf - sg->offset, KM_IRQ0); @@ -1893,16 +1851,16 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (sg_count == 0) goto out; - for (i = 0; i < sg_count; i++) { - command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(&sglist[i])); - command_packet->sg_list[i].length = cpu_to_le32(sg_dma_len(&sglist[i])); + scsi_for_each_sg(srb, sg, sg_count, i) { + command_packet->sg_list[i].address = TW_CPU_TO_SGL(sg_dma_address(sg)); + command_packet->sg_list[i].length = cpu_to_le32(sg_dma_len(sg)); if (command_packet->sg_list[i].address & TW_CPU_TO_SGL(TW_ALIGNMENT_9000_SGL)) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x2e, "Found unaligned sgl address during execute scsi"); goto out; } } } - command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), tw_dev->srb[request_id]->use_sg)); + command_packet->sgl_entries__lunh = cpu_to_le16(TW_REQ_LUN_IN((srb->device->lun >> 4), scsi_sg_count(tw_dev->srb[request_id]))); } } else { /* Internal cdb post */ @@ -1932,7 +1890,7 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, /* Update SG statistics */ if (srb) { - tw_dev->sgl_entries = tw_dev->srb[request_id]->use_sg; + tw_dev->sgl_entries = scsi_sg_count(tw_dev->srb[request_id]); if (tw_dev->sgl_entries > tw_dev->max_sgl_entries) tw_dev->max_sgl_entries = tw_dev->sgl_entries; } @@ -1951,16 +1909,13 @@ out: /* This function completes an execute scsi operation */ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id) { - if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH && - (tw_dev->srb[request_id]->sc_data_direction == DMA_FROM_DEVICE || - tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL)) { - if (tw_dev->srb[request_id]->use_sg == 0) { - memcpy(tw_dev->srb[request_id]->request_buffer, - tw_dev->generic_buffer_virt[request_id], - tw_dev->srb[request_id]->request_bufflen); - } - if (tw_dev->srb[request_id]->use_sg == 1) { - struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + + if (scsi_bufflen(cmd) < TW_MIN_SGL_LENGTH && + (cmd->sc_data_direction == DMA_FROM_DEVICE || + cmd->sc_data_direction == DMA_BIDIRECTIONAL)) { + if (scsi_sg_count(cmd) == 1) { + struct scatterlist *sg = scsi_sglist(tw_dev->srb[request_id]); char *buf; unsigned long flags = 0; local_irq_save(flags); @@ -2017,16 +1972,8 @@ static char *twa_string_lookup(twa_message_type *table, unsigned int code) static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id) { struct scsi_cmnd *cmd = tw_dev->srb[request_id]; - struct pci_dev *pdev = tw_dev->tw_pci_dev; - switch(cmd->SCp.phase) { - case TW_PHASE_SINGLE: - pci_unmap_single(pdev, cmd->SCp.have_data_in, cmd->request_bufflen, DMA_BIDIRECTIONAL); - break; - case TW_PHASE_SGLIST: - pci_unmap_sg(pdev, cmd->request_buffer, cmd->use_sg, DMA_BIDIRECTIONAL); - break; - } + scsi_dma_unmap(cmd); } /* End twa_unmap_scsi_data() */ /* scsi_host_template initializer */ @@ -2063,11 +2010,14 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id pci_set_master(pdev); - retval = pci_set_dma_mask(pdev, sizeof(dma_addr_t) > 4 ? DMA_64BIT_MASK : DMA_32BIT_MASK); - if (retval) { - TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); - goto out_disable_device; - } + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) + || pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) + || pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { + TW_PRINTK(host, TW_DRIVER, 0x23, "Failed to set dma mask"); + retval = -ENODEV; + goto out_disable_device; + } host = scsi_host_alloc(&driver_template, sizeof(TW_Device_Extension)); if (!host) { @@ -2115,7 +2065,8 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id goto out_iounmap; /* Set host specific parameters */ - if (pdev->device == PCI_DEVICE_ID_3WARE_9650SE) + if ((pdev->device == PCI_DEVICE_ID_3WARE_9650SE) || + (pdev->device == PCI_DEVICE_ID_3WARE_9690SA)) host->max_id = TW_MAX_UNITS_9650SE; else host->max_id = TW_MAX_UNITS; @@ -2222,6 +2173,8 @@ static struct pci_device_id twa_pci_tbl[] __devinitdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9650SE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9690SA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { } }; MODULE_DEVICE_TABLE(pci, twa_pci_tbl); |