/*======================================================================/ A driver for the MACNICA SCSI mPS110 card mPS110 is very similar to qlogic scsi card. mPS110 may be used a SYM53C500 SCSI protocol controller, whitch is similar to NCR53C406 ISA SCSI protocol controller. This driver referred some parts of following code. qlogic.c: qlogic linux driver written by Tom Zerucha,(zerucha@shell.portal.com) qlogic_cs.c: qlogic pcmcia scsi driver written by David Hinds,(dhinds@allegro.stanford.edu) This software may be used and distributed according to the terms of the GNU Public License. Written by N.Katayama (kata-n@po.iijnet.or.jp) ======================================================================*/ #include #include #ifdef MODULE #define init_mps110_cs init_module #endif #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= VERSION(1,3,98)) #include #else #include #endif #include #include BLK_DEV_HDR #include "/usr/src/linux/drivers/scsi/scsi.h" #include "/usr/src/linux/drivers/scsi/hosts.h" #if (LINUX_VERSION_CODE >= VERSION(1,3,98)) #include #else #include #endif #include #include #include #include #include #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if(pc_debug > (n)) printk(KERN_DEBUG args); static char *version = "mps110_cs.c 1.02 (N.Katayama)"; #else #define DEBUG(n, args...) #endif /*====================================================================*/ int mps110_detect(Scsi_Host_Template * ); const char * mps110_info(struct Scsi_Host *); int mps110_command(Scsi_Cmnd *); int mps110_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *)); int mps110_abort(Scsi_Cmnd *); int mps110_reset(Scsi_Cmnd *); #if (LINUX_VERSION_CODE > VERSION(1,2,13)) int mps110_biosparam(Disk * , kdev_t, int []); #else int mps110_biosparam(Disk * , int, int []); #endif static void mps_interrupt( void * ); static unsigned int mp_pcmd(Scsi_Cmnd * ); #ifndef NULL #define NULL (0) #endif #if (LINUX_VERSION_CODE > VERSION(1,2,13)) #if (LINUX_VERSION_CODE >= VERSION(2,2,0)) #define MPS110 { info: mps110_info, \ detect: mps110_detect, \ command: mps110_command, \ queuecommand: mps110_queuecommand, \ abort: mps110_abort, \ reset: mps110_reset, \ bios_param: mps110_biosparam, \ can_queue: 1, \ this_id: -1, \ sg_tablesize: SG_ALL, \ cmd_per_lun: 1, \ use_clustering: DISABLE_CLUSTERING } #else #define MPS110 { \ /* next */ NULL, \ /* usage_count */ NULL, \ /* proc_dir */ NULL, \ /* proc_info */ NULL, \ /* name */ NULL, \ /* detect */ mps110_detect, \ /* release */ NULL, \ /* info */ mps110_info, \ /* command */ mps110_command, \ /* queuecommand */ mps110_queuecommand, \ /* abort */ mps110_abort, \ /* reset */ mps110_reset, \ /* slave_attach */ NULL, \ /* bios_param */ mps110_biosparam, \ /* can_queue */ 1, \ /* this_id */ -1, \ /* sg_tablesize */ SG_ALL, \ /* cmd_per_lun */ 1, \ /* present */ 0, \ /* unchecked_isa_dma */ 0, \ DISABLE_CLUSTERING \ } #endif #else #define MPS110 { \ NULL, \ NULL, \ NULL, \ mps110_detect, \ NULL, \ mps110_info, \ mps110_command, \ mps110_queuecommand, \ mps110_abort, \ mps110_reset, \ NULL, \ NULL, \ 1, \ -1, \ SG_ALL, \ 1, \ 0, \ 0, \ DISABLE_CLUSTERING \ } #endif /* 53C9x family command */ #define NOP_53C 0x00 #define CLR_FIFO 0x01 #define SOFT_RESET 0x02 #define SCSI_RESET 0x03 #define CLEAR_ACK 0x12 #define DMA_TRANSFER_INFO 0x90 #define RECEIVE_MSG 0x28 #define SEND_MESSAGE 0x20 #define SET_ATN 0x1a #define TRANSFER_INFO 0x10 #define GET_STAT 0x11 /* 53C9x register */ #define TC_LOW 0x00 /* transfer counter LSB */ #define TC_HIGH 0x01 /* transfer counter MSB */ #define FIFO 0x02 /* FIFO register */ #define CMD_REG 0x03 /* command register */ #define STAT_REG 0x04 /* status register */ #define DEST_ID 0x04 /* selection/reselection BUS ID */ #define INT_REG 0x05 /* interrupt status register */ #define SRTIMOUT 0x05 /* select/reselect timeout reg */ #define SEQ_REG 0x06 /* sequence step register */ #define SYNCPRD 0x06 /* synchronous transfer period */ #define FLAGS_REG 0x07 /* indicates # of bytes in fifo */ #define SYNCOFF 0x07 /* synchronous offset register */ #define CONFIG1 0x08 /* configuration register */ #define CLKCONV 0x09 /* clock conversion reg */ #define TESTREG 0x0a /* test mode register */ #define CONFIG2 0x0b /* Configuration 2 Register */ #define CONFIG3 0x0c /* Configuration 3 Register */ #define FIFO_BOTTOM 0x0f /* Reserve FIFO byte register */ /*====================================================================*/ /* Parameters that can be set with 'insmod' */ /* Bit map of interrupts to choose from */ /* This driver never use IRQ */ static u_long irq_mask = 0x0000; /* static u_long irq_mask = 0xdeb8; */ /* if you not use MO, set fast_pio = 1 and sg_tablesize = 64 */ static int fast_pio = 1; static int sg_tablesize = SG_ALL; /*====================================================================*/ typedef struct scsi_info_t { u_short manf_id; int ndev; dev_node_t node[8]; } scsi_info_t; static void mps110_release(u_long arg); static int mps110_event(event_t event, int priority, event_callback_args_t *args); static dev_link_t *mps110_attach(void); static void mps110_detach(dev_link_t *); static Scsi_Host_Template driver_template = MPS110; static dev_link_t *dev_list = NULL; static dev_info_t dev_info = "mps110_cs"; /*----------------------------------------------------------------*/ #include #if (LINUX_VERSION_CODE > VERSION(1,2,13)) #include /* to get disk capacity */ #endif #include #include #include #include #include #include #include #include #include "/usr/src/linux/drivers/scsi/sd.h" #include #if (LINUX_VERSION_CODE > VERSION(1,2,13)) /* struct proc_dir_entry proc_scsi_mps110 = { PROC_SCSI_NOT_PRESENT, 6, "mps110", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; */ struct proc_dir_entry proc_scsi_mps110 = { PROC_SCSI_NCR53C406A, 6, "mps110", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; #endif /*----------------------------------------------------------------*/ /* driver state info, local to driver */ static int mbase = 0; /* Port */ static int minitid; /* initiator ID */ static int mabort; /* Flag to cause an abort */ static int mpirq = -1; /* IRQ being used */ static char minfo[80]; /* description */ static Scsi_Cmnd *mpcmd; /* current command being processed */ static struct tq_struct mps_tq = {0,0,mps_interrupt,NULL}; /* task queue entry */ /*----------------------------------------------------------------*/ /* The mps110 card uses two register maps - These macros select which one */ /*----------------------------------------------------------------*/ /* 24bit transfer counter */ #define REG0 ( outb( inb( mbase + 0xd ) & 0x7f , mbase + 0xd ), outb( 4 , mbase + 0xd )) #define REG1 ( outb( inb( mbase + 0xd ) | 0x80 , mbase + 0xd ), outb( 0xb4, mbase + 0xd )) /* 16bit transfer counter */ /* #define REG0 ( outb( 4 , mbase + 0xd )) #define REG1 ( outb( 0x80, mbase + 0xd )) */ /* following is watchdog timeout in microseconds */ #define WATCHDOG 10000000 /*----------------------------------------------------------------*/ /* local functions */ /*----------------------------------------------------------------*/ static inline void mp_zap(void); /* error recovery - reset everything */ void mp_zap() { int x; unsigned long flags; save_flags( flags ); cli(); x = inb(mbase + 0xd); REG0; outb(SCSI_RESET, mbase + CMD_REG); /* reset SCSI */ outb(SOFT_RESET, mbase + CMD_REG); /* reset chip */ if (x & 0x80) REG1; restore_flags( flags ); } static void mps_interrupt( void *data ) { Scsi_Cmnd *cmd; int k; cmd = mpcmd; if( !cmd ) return; if( mabort ) { if( mabort == 1 ) { cmd->result = DID_ABORT << 16; } else { cmd->result = DID_RESET << 16; } mpcmd = NULL; cmd->scsi_done(cmd); return; } if( !(k=inb(mbase+STAT_REG) & 0xe0) ) { queue_task( &mps_tq, &tq_scheduler ); return; } if (k & 0x60) mp_zap(); if (k & 0x20) { cmd->result = DID_PARITY << 16; mpcmd = NULL; cmd->scsi_done(cmd); return; } if (k & 0x40) { cmd->result = DID_ERROR << 16; mpcmd = NULL; cmd->scsi_done(cmd); return; } cmd->result = mp_pcmd(cmd); mpcmd = NULL; cmd->scsi_done(cmd); return; } /*----------------------------------------------------------------*/ /* do pseudo-dma */ /*----------------------------------------------------------------*/ static inline int mp_pdma(int phase, char *request, int reqlen) { int j; j = 0; if (phase & 1) { /* in */ /* empty fifo in large chunks */ if( fast_pio ) { if( reqlen >= 128 && (inb( mbase + 8 ) & 2) ) { /* full */ insl( mbase + 4, request, 32 ); reqlen -= 128; request += 128; } while( reqlen >= 84 && !( j & 0xc0 ) ) /* 2/3 */ if( (j=inb( mbase + 8 )) & 4 ) { insl( mbase + 4, request, 21 ); reqlen -= 84; request += 84; } if( reqlen >= 44 && (inb( mbase + 8 ) & 8) ) { /* 1/3 */ insl( mbase + 4, request, 11 ); reqlen -= 44; request += 44; } } /* until both empty and int (or until reclen is 0) */ j = 0; while( reqlen && !( (j & 0x10) && (j & 0xc0) ) ) { /* while bytes to receive and not empty */ j &= 0xc0; while ( reqlen && !( (j=inb(mbase + 8)) & 0x10 ) ) { *request++ = inb(mbase + 4); reqlen--; } if( j & 0x10 ) j = inb(mbase+8); } } else { /* out */ if( fast_pio ) { if( reqlen >= 128 && inb( mbase + 8 ) & 0x10 ) { /* empty */ outsl(mbase + 4, request, 32 ); reqlen -= 128; request += 128; } while( reqlen >= 84 && !( j & 0xc0 ) ) /* 1/3 */ if( !((j=inb( mbase + 8 )) & 8) ) { outsl( mbase + 4, request, 21 ); reqlen -= 84; request += 84; } if( reqlen >= 40 && !(inb( mbase + 8 ) & 4 ) ) { /* 2/3 */ outsl( mbase + 4, request, 10 ); reqlen -= 40; request += 40; } } /* until full and int (or until reclen is 0) */ j = 0; while( reqlen && !( (j & 2) && (j & 0xc0) ) ) { /* while bytes to send and not full */ while ( reqlen && !( (j=inb(mbase + 8)) & 2 ) ) { outb(*request++, mbase + 4); reqlen--; } if( j & 2 ) j = inb(mbase+8); } } /* maybe return reqlen */ return inb( mbase + 8 ) & 0xc0; } /*----------------------------------------------------------------*/ /* wait for interrupt flag (polled - not real hardware interrupt) */ /*----------------------------------------------------------------*/ static inline int mp_wai(void) { int i,k=0; /* unsigned long flags; */ i = jiffies + WATCHDOG; /* save_flags( flags ); sti(); */ while ( i > jiffies && !mabort && !((k = inb(mbase + STAT_REG)) & 0xe0)) { barrier(); } /* restore_flags( flags ); */ if (i <= jiffies) { return (DID_TIME_OUT); } if (mabort) return (mabort == 1 ? DID_ABORT : DID_RESET); if (k & 0x60) mp_zap(); if (k & 0x20) return (DID_PARITY); if (k & 0x40) return (DID_ERROR); return 0; } /*----------------------------------------------------------------*/ /* initiate scsi command */ /*----------------------------------------------------------------*/ static inline void mp_icmd(Scsi_Cmnd * cmd) { unsigned int i; unsigned long flags; mabort = 0; save_flags( flags ); cli(); REG0; inb( mbase + INT_REG ); if( inb( mbase + INT_REG) ) { outb(SOFT_RESET, mbase + CMD_REG); /* reset chip */ outb(NOP_53C, mbase + CMD_REG); /* nop */ } else if( inb( mbase + 7 ) & 0x1f ) { outb(CLR_FIFO, mbase + CMD_REG ); /* clear fifo */ } while( inb(mbase + INT_REG) ); /* clear intr. */ /* outb( 0x40 | minitid | 0x10 , mbase + CONFIG1); */ outb( minitid, mbase + CONFIG1); outb( 8, mbase + CLKCONV); /* clock conv. factor 40MHz/5 */ /* outb( 5, mbase + CLKCONV); /+ clock conv. factor 25MHz/5 */ outb(0x48, mbase + CONFIG2 ); outb(0x08, mbase + CONFIG3 ); REG1; outb(0x00, mbase + 0x09); /* Disable IRQ ???? */ outb(0, mbase + 0x0b ); outb(1, mbase + 0x08); /* set for PIO pseudo DMA */ inb(mbase + 8); /* clear int bits */ REG0; outb(0x40, mbase + 0x0b ); outb(cmd->target , mbase + DEST_ID ); /* 0x04 */ outb(0x99, mbase + SRTIMOUT); /* select/reselect timer */ outb(0x00, mbase + SYNCOFF); /* set sync. offset */ outb(0x05, mbase + SYNCPRD); /* set sync. priod */ outb( CLR_FIFO, mbase + CMD_REG ) ; outb( 0x80, mbase + FIFO); /* Identify message (DiscPriv=NO) */ for (i = 0; i < cmd->cmd_len; i++) { outb(cmd->cmnd[i], mbase + FIFO); } mpcmd = cmd; outb(0x42, mbase + CMD_REG); /* select and send command with ATN */ restore_flags( flags ); } /*----------------------------------------------------------------*/ /* process scsi command - usually after interrupt */ /*----------------------------------------------------------------*/ static unsigned int mp_pcmd(Scsi_Cmnd * cmd) { unsigned int i, j, k; unsigned int result; /* ultimate return result */ unsigned int status; /* scsi returned status */ unsigned int message; /* scsi returned message */ unsigned int phase; /* recorded scsi phase */ unsigned int reqlen; /* total length of transfer */ struct scatterlist *sglist; /* scatter-gather list pointer */ unsigned int sgcount; /* sg counter */ j = inb(mbase + SEQ_REG); i = inb(mbase + INT_REG); if (i == 0x20) { return (DID_NO_CONNECT << 16); } i |= inb(mbase + INT_REG); /* the 0x10 bit can be set after the 0x08 */ if (i != 0x18) { printk("Mps:Bad Interrupt status:%02x\n", i); mp_zap(); return (DID_BAD_INTR << 16); } j &= 7 ; if(j != 3 && j != 4) { printk("Mps:Bad sequence for command %d, int %02X, cmdleft = %d\n", j, i, inb( mbase+7 ) & 0x1f ); mp_zap(); return (DID_ERROR << 16); } result = DID_OK; if (inb(mbase + FLAGS_REG) & 0x1f) /* if some bytes in fifo */ outb(CLR_FIFO, mbase + CMD_REG); /* clear fifo */ reqlen = cmd->request_bufflen; if (reqlen && !((phase = inb(mbase + STAT_REG)) & 6)) { /* data phase */ outb(reqlen, mbase + TC_LOW ); /* low-mid xfer cnt */ outb(reqlen >> 8, mbase + TC_HIGH); /* low-mid xfer cnt */ outb(reqlen >> 16, mbase + 0x0e); /* high xfer cnt */ /* REG1; if( phase & 1 ) { outb(0x00, mbase + 0x0b); } else { outb(0x01, mbase + 0x0b); } REG0; */ outb(DMA_TRANSFER_INFO, mbase + CMD_REG); /* PIO pseudo DMA to buffer or sglist */ REG1; if (!cmd->use_sg) { /* not use Scatter & gather list */ mp_pdma(phase, cmd->request_buffer, cmd->request_bufflen); } else { /* use Scatter & gather list */ sgcount = cmd->use_sg; sglist = cmd->request_buffer; while (sgcount--) { if (mabort) { REG0; return ((mabort == 1 ? DID_ABORT : DID_RESET) << 16); } if(mp_pdma(phase, sglist->address, sglist->length)) { break; } sglist++; } } REG0; if ((k = mp_wai())) { return (k << 16); } k = inb(mbase + INT_REG); /* should be 0x10, bus service */ } k = jiffies + WATCHDOG; while ( k > jiffies && !mabort && !(inb(mbase + 4) & 6)); /* wait for status phase */ if ( k <= jiffies ) { mp_zap(); return (DID_TIME_OUT << 16); } while (inb(mbase + INT_REG)); /* clear pending ints */ if (mabort) return ((mabort == 1 ? DID_ABORT : DID_RESET) << 16); outb(GET_STAT, mbase + CMD_REG); /* get status and message */ if ((k = mp_wai())) return (k << 16); i = inb(mbase + INT_REG); /* get chip irq stat */ j = inb(mbase + FLAGS_REG) & 0x1f; /* and bytes rec'd */ status = inb(mbase + FIFO); message = inb(mbase + FIFO); /* should get function complete int if Status and message, else bus serv if only status */ if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) { printk("Mps:Error during status phase, int=%02X, %d bytes recd\n", i, j); result = DID_ERROR; } outb(CLEAR_ACK, mbase + CMD_REG); /* done, disconnect */ if ((k = mp_wai())) return (k << 16); /* should get bus service interrupt and disconnect interrupt */ i = inb(mbase + INT_REG); /* should be bus service */ while (!mabort && ((i & 0x20) != 0x20)) { barrier(); i |= inb(mbase + INT_REG); } if (mabort) return ((mabort == 1 ? DID_ABORT : DID_RESET) << 16); return (result << 16) | (message << 8) | (status & STATUS_MASK); } /*----------------------------------------------------------------*/ /* global functions */ /*----------------------------------------------------------------*/ /* command process */ int mps110_command(Scsi_Cmnd * cmd) { int k; int rtn ; sti(); if (cmd->target == minitid) return (DID_BAD_TARGET << 16); mp_icmd(cmd); if ((k = mp_wai())) { mpcmd = NULL; return (k << 16); } rtn = mp_pcmd(cmd); mpcmd = NULL; return rtn; } int mps110_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)) { if( mpcmd ) return 0; sti(); cmd->scsi_done = done; if( cmd->target == minitid ) { cmd->result = DID_BAD_TARGET << 16; mpcmd = NULL; done(cmd); return 0; } mp_icmd(cmd); queue_task(&mps_tq,&tq_scheduler); return 0; } /*----------------------------------------------------------------*/ /* look for mps110 card and init if found */ /*----------------------------------------------------------------*/ int mps110_detect(Scsi_Host_Template * host) { struct Scsi_Host *hreg; /* registered host structure */ #if (LINUX_VERSION_CODE > VERSION(1,2,13)) host->proc_dir = &proc_scsi_mps110; #endif minitid = host->this_id; if (minitid < 0) minitid = 7; /* if no ID, use 7 */ request_region( mbase , 0x10 ,"mps110"); hreg = scsi_register( host , 0 ); /* no host data */ hreg->io_port = mbase; hreg->n_io_port = 16; hreg->dma_channel = -1; if( mpirq != -1 ) hreg->irq = mpirq; sprintf(minfo, "mPS110 Driver version 1.00, iobase at %03X", mbase ); host->name = minfo; return 1; } /*----------------------------------------------------------------*/ /* return bios parameters */ /*----------------------------------------------------------------*/ #if (LINUX_VERSION_CODE > VERSION(1,2,13)) int mps110_biosparam(Disk * disk, kdev_t dev, int ip[]) #else int mps110_biosparam(Disk * disk, int dev, int ip[]) #endif { ip[0] = 0x40; ip[1] = 0x20; ip[2] = disk->capacity / (ip[0] * ip[1]); if (ip[2] > 1024) { ip[0] = 0xff; ip[1] = 0x3f; ip[2] = disk->capacity / (ip[0] * ip[1]); if (ip[2] > 1023) ip[2] = 1023; } return 0; } /*----------------------------------------------------------------*/ /* abort command in progress */ /*----------------------------------------------------------------*/ int mps110_abort(Scsi_Cmnd * cmd) { mabort = 1; mp_zap(); return 0; } /*----------------------------------------------------------------*/ /* reset SCSI bus */ /*----------------------------------------------------------------*/ int mps110_reset(Scsi_Cmnd * cmd) { mabort = 2; mp_zap(); return 1; } /*----------------------------------------------------------------*/ /* return info string */ /*----------------------------------------------------------------*/ const char *mps110_info(struct Scsi_Host * host) { return minfo; } /*====================================================================*/ static void cs_error(client_handle_t handle, int func, int ret) { error_info_t err = { func, ret }; CardServices(ReportError, handle, &err); } /*====================================================================*/ static dev_link_t *mps110_attach(void) { client_reg_t client_reg; dev_link_t *link; int ret; DEBUG(0, "mps110_attach()\n"); /* Create new SCSI device */ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); memset(link, 0, sizeof(struct dev_link_t)); link->priv = kmalloc(sizeof(struct scsi_info_t), GFP_KERNEL); memset(link->priv, 0, sizeof(struct scsi_info_t)); link->release.function = &mps110_release; link->release.data = (u_long)link; link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.IOAddrLines = 10; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; link->irq.IRQInfo2 = irq_mask; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; /* Register with Card Services */ link->next = dev_list; dev_list = link; client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.event_handler = &mps110_event; client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != 0) { cs_error(link->handle, RegisterClient, ret); mps110_detach(link); return NULL; } return link; } /* mps110_attach */ /*====================================================================*/ static void mps110_detach(dev_link_t *link) { dev_link_t **linkp; DEBUG(0, "mps110_detach(0x%p)\n", link); /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; if (*linkp == NULL) return; if (link->state & DEV_CONFIG) { mps110_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { link->state |= DEV_STALE_LINK; return; } } if (link->handle) CardServices(DeregisterClient, link->handle); /* Unlink device structure, free bits */ *linkp = link->next; if (link->priv) { kfree_s(link->priv, sizeof(struct scsi_info_t)); } kfree_s(link, sizeof(struct dev_link_t)); } /* mps110_detach */ /*====================================================================*/ #define CS_CHECK(fn, args...) \ while((last_ret = CardServices(last_fn=(fn), args))!=0) goto cs_failed #define CFG_CHECK(fn,args...) \ if(CardServices(fn, args) != 0) goto next_entry static void mps110_config(dev_link_t *link) { client_handle_t handle; scsi_info_t *info; tuple_t tuple; cisparse_t parse; int i, last_ret, last_fn; u_char tuple_data[64]; Scsi_Device *dev; dev_node_t **tail, *node; #if (LINUX_VERSION_CODE >= VERSION(2,1,75)) struct Scsi_Host *host; #endif handle = link->handle; info = link->priv; DEBUG(0, "mps110_config(0x%p)\n", link); tuple.DesiredTuple = CISTPL_CONFIG; tuple.TupleData = (cisdata_t *)tuple_data; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; CS_CHECK(GetFirstTuple, handle, &tuple); CS_CHECK(GetTupleData, handle, &tuple); CS_CHECK(ParseTuple, handle, &tuple, &parse); link->conf.ConfigBase = parse.config.base; tuple.DesiredTuple = CISTPL_MANFID; if ((CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) && (CardServices(GetTupleData, handle, &tuple) == CS_SUCCESS)) info->manf_id = le16_to_cpu(tuple.TupleData[0]); /* Configure card */ #if (LINUX_VERSION_CODE >= VERSION(2,1,23)) driver_template.module = &__this_module; #else driver_template.usage_count = &GET_USE_COUNT(&__this_module); #endif link->state |= DEV_CONFIG; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); for(;;) { CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); link->conf.ConfigIndex = parse.cftable_entry.index; link->io.BasePort1 = parse.cftable_entry.io.win[0].base; if(link->io.BasePort1 != 0) if(CS_SUCCESS == CardServices(RequestIO, link->handle, &link->io)) break; next_entry: CS_CHECK(GetNextTuple, handle, &tuple); } /* CS_CHECK(RequestIRQ, handle, &link->irq); */ CS_CHECK(RequestConfiguration, link->handle, &link->conf); #if 0 if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == 0x0098)) { /* set ATAcmd */ outb( 0xb4, link->io.BasePort1+0xd); outb( 0x24, link->io.BasePort1+0x9); outb( 0x04, link->io.BasePort1+0xd); } #endif /* A bad hack... */ release_region(link->io.BasePort1, link->io.NumPorts1); /* Set port and IRQ */ mbase=link->io.BasePort1 ; mpirq=-1; /* set sg_tablesize */ if( sg_tablesize >0 && sg_tablesize <= 64 ) driver_template.sg_tablesize = sg_tablesize; scsi_register_module(MODULE_SCSI_HA, &driver_template); tail = &link->dev; info->ndev = 0; #if (LINUX_VERSION_CODE < VERSION(2,1,75)) for (dev = scsi_devices; dev != NULL; dev = dev->next) if (dev->host->hostt == &driver_template) { #else for(host = scsi_hostlist; host; host = host->next) if(host->hostt == &driver_template) for(dev = host->host_queue; dev ; dev = dev->next) { #endif u_long arg[2], id; kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg); id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) + ((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000); node = &info->node[info->ndev]; node->minor = 0; switch (dev->type) { case TYPE_TAPE: node->major = SCSI_TAPE_MAJOR; sprintf(node->dev_name, "st#%04lx", id); break; case TYPE_DISK: case TYPE_MOD: node->major = SCSI_DISK0_MAJOR; sprintf(node->dev_name, "sd#%04lx", id); break; case TYPE_ROM: case TYPE_WORM: node->major = SCSI_CDROM_MAJOR; sprintf(node->dev_name, "sr#%04lx", id); break; default: node->major = SCSI_GENERIC_MAJOR; sprintf(node->dev_name, "sg#%04lx", id); break; } *tail = node; tail = &node->next; info->ndev++; } *tail = NULL; if (info->ndev == 0) printk(KERN_INFO "mps110_cs: no SCSI devices found\n"); #if 0 strcpy(info->node[0].dev_name, "n/a"); link->dev = &info->node[0]; #endif /* GET_SCSI_INFO */ link->state &= ~DEV_CONFIG_PENDING; return; cs_failed: cs_error(link->handle, last_fn, last_ret); mps110_release((u_long)link); return; } /* mps110_config */ /*====================================================================*/ static void mps110_release(u_long arg) { dev_link_t *link = (dev_link_t *)arg; DEBUG(0, "mps110_release(0x%p)\n", link); #if (LINUX_VERSION_CODE < VERSION(2,1,23)) if (*driver_template.usage_count != 0) { #else if (GET_USE_COUNT(driver_template.module) != 0) { #endif DEBUG(1, "mps110_cs: release postponed, " "device still open\n"); link->state |= DEV_STALE_CONFIG; return; } scsi_unregister_module(MODULE_SCSI_HA, &driver_template); link->dev = NULL; CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG; if (link->state & DEV_STALE_LINK) mps110_detach(link); } /* mps110_release */ /*====================================================================*/ static int mps110_event(event_t event, int priority, event_callback_args_t *args) { dev_link_t *link = args->client_data; DEBUG(0, "mps110_event()\n"); switch (event) { #ifdef PCMCIA_DEBUG case CS_EVENT_REGISTRATION_COMPLETE: if (pc_debug) printk(KERN_DEBUG "mps110_cs: registration complete\n"); break; #endif case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { link->release.expires = RUN_AT(HZ/20); add_timer(&link->release); } break; case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; mps110_config(link); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: if (link->state & DEV_CONFIG) CardServices(ReleaseConfiguration, link->handle); break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ case CS_EVENT_CARD_RESET: if (link->state & DEV_CONFIG) { CardServices(RequestConfiguration, link->handle, &link->conf); mps110_reset(NULL); } break; } return 0; } /* mps110_event */ /*====================================================================*/ int init_mps110_cs(void) { servinfo_t serv; DEBUG(0, "%s\n", version); CardServices(GetCardServicesInfo, &serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "mps110_cs: Card Services release " "does not match!\n"); return -1; } register_pcmcia_driver(&dev_info, &mps110_attach, &mps110_detach); return 0; } #ifdef MODULE void cleanup_module(void) { DEBUG(0, "mps110_cs: unloading\n"); unregister_pcmcia_driver(&dev_info); while (dev_list != NULL) mps110_detach(dev_list); } #endif