📄 cpqfctsinit.c
字号:
Scsi_Device *SDpnt; Scsi_Cmnd *ScsiPassThruCmnd; unsigned long flags; ENTER("cpqfcTS_ioctl"); // can we find an FC device mapping to this SCSI target? DumCmnd.channel = ScsiDev->channel; // For searching DumCmnd.target = ScsiDev->id; pLoggedInPort = fcFindLoggedInPort( fcChip, &DumCmnd, // search Scsi Nexus 0, // DON'T search linked list for FC port id NULL, // DON'T search linked list for FC WWN NULL); // DON'T care about end of list if( pLoggedInPort == NULL ) // not found! { result = -ENXIO; } else // we know what FC device to operate on... { switch (Cmnd) { // Passthrough provides a mechanism to bypass the RAID // or other controller and talk directly to the devices // (e.g. physical disk drive) // Passthrough commands, unfortunately, tend to be vendor // specific; this is tailored to COMPAQ's RAID (RA4x00) case CPQFCTS_SCSI_PASSTHRU: { void *buf = NULL; // for kernel space buffer for user data if( !arg) return -EINVAL; // must be super user to send stuff directly to the // controller and/or physical drives... if( !suser() ) return -EPERM; // copy the caller's struct to our space. copy_from_user_ret( &ioc, arg, sizeof( VENDOR_IOCTL_REQ), -EFAULT); vendor_cmd = ioc.argp; // i.e., CPQ specific command struct // If necessary, grab a kernel/DMA buffer if( vendor_cmd->len) { buf = kmalloc( vendor_cmd->len, GFP_KERNEL); if( !buf) return -ENOMEM; } // Now build a SCSI_CMND to pass down... // This function allocates and sets Scsi_Cmnd ptrs such as // ->channel, ->target, ->host ScsiPassThruCmnd = scsi_allocate_device(NULL, ScsiDev, 1); // Need data from user? // make sure caller's buffer is in kernel space. if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) && vendor_cmd->len) copy_from_user_ret( buf, vendor_cmd->bufp, vendor_cmd->len, -EFAULT); // copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below) memcpy( &ScsiPassThruCmnd->cmnd[0], &vendor_cmd->cdb[0], MAX_COMMAND_SIZE); // we want to copy all 16 bytes into the FCP-SCSI CDB, // although the actual passthru only uses up to the // first 12. ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB // Unfortunately, the SCSI command cmnd[] field has only // 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased // to 16 for newer Fibre Channel and SCSI-3 larger CDBs. // However, to avoid a mandatory kernel rebuild, we use the SCp // spare field to store the extra 4 bytes ( ugly :-( if( MAX_COMMAND_SIZE < 16) { memcpy( &ScsiPassThruCmnd->SCp.buffers_residual, &vendor_cmd->cdb[12], 4); } ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU! // suppress LUN masking // and VSA logic // Use spare fields to copy FCP-SCSI LUN address info... ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus; ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive; // We copy the scheme used by scsi.c to submit commands // to our own HBA. We do this in order to stall the // thread calling the IOCTL until it completes, and use // the same "_quecommand" function for synchronizing // FC Link events with our "worker thread". spin_lock_irqsave(&io_request_lock, flags); { DECLARE_MUTEX_LOCKED(sem); ScsiPassThruCmnd->request.sem = &sem; // eventually gets us to our own _quecommand routine scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0], buf, vendor_cmd->len, my_ioctl_done, 10*HZ, 1);// timeout,retries spin_unlock_irqrestore(&io_request_lock, flags); // Other I/Os can now resume; we wait for our ioctl // command to complete down(&sem); spin_lock_irqsave(&io_request_lock, flags); ScsiPassThruCmnd->request.sem = NULL; } result = ScsiPassThruCmnd->result; // copy any sense data back to caller if( result != 0 ) { memcpy( vendor_cmd->sense_data, // see struct def - size=40 ScsiPassThruCmnd->sense_buffer, sizeof(ScsiPassThruCmnd->sense_buffer)); } SDpnt = ScsiPassThruCmnd->device; scsi_release_command(ScsiPassThruCmnd); // "de-allocate" ScsiPassThruCmnd = NULL; if (!SDpnt->was_reset && SDpnt->scsi_request_fn) (*SDpnt->scsi_request_fn)(); wake_up(&SDpnt->device_wait); spin_unlock_irqrestore(&io_request_lock, flags); // need to pass data back to user (space)? if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) && vendor_cmd->len ) copy_to_user_ret( vendor_cmd->bufp, buf, vendor_cmd->len, -EFAULT); if( buf) kfree( buf); return result; } case CPQFCTS_GETPCIINFO: { cpqfc_pci_info_struct pciinfo; if( !arg) return -EINVAL; pciinfo.bus = cpqfcHBAdata->PciDev->bus->number; pciinfo.dev_fn = cpqfcHBAdata->PciDev->devfn; pciinfo.board_id = cpqfcHBAdata->PciDev->device | (cpqfcHBAdata->PciDev->vendor <<16); copy_to_user_ret( arg, &pciinfo, sizeof(cpqfc_pci_info_struct), -EFAULT); return 0; } case CPQFCTS_GETDRIVVER: { DriverVer_type DriverVer = CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR); if( !arg) return -EINVAL; copy_to_user_ret( arg, &DriverVer, sizeof(DriverVer), -EFAULT); return 0; } case SCSI_IOCTL_FC_TARGET_ADDRESS: result = verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)); if (result) break; put_user(pLoggedInPort->port_id, &((Scsi_FCTargAddress *) arg)->host_port_id); for( i=3,j=0; i>=0; i--) // copy the LOGIN port's WWN put_user(pLoggedInPort->u.ucWWN[i], &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); for( i=7; i>3; i--) // copy the LOGIN port's WWN put_user(pLoggedInPort->u.ucWWN[i], &((Scsi_FCTargAddress *) arg)->host_wwn[j++]); break; default: result = -EINVAL; break; } } LEAVE("cpqfcTS_ioctl"); return result;}/* "Release" the Host Bus Adapter... disable interrupts, stop the HBA, release the interrupt, and free all resources */int cpqfcTS_release(struct Scsi_Host *HostAdapter){ CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; ENTER("cpqfcTS_release"); DEBUG_PCI( printk(" cpqfcTS: delete timer...\n")); del_timer( &cpqfcHBAdata->cpqfcTStimer); // disable the hardware... DEBUG_PCI( printk(" disable hardware, destroy queues, free mem\n")); cpqfcHBAdata->fcChip.ResetTachyon( cpqfcHBAdata, CLEAR_FCPORTS); // kill kernel thread if( cpqfcHBAdata->worker_thread ) // (only if exists) { DECLARE_MUTEX_LOCKED(sem); // synchronize thread kill cpqfcHBAdata->notify_wt = &sem; DEBUG_PCI( printk(" killing kernel thread\n")); send_sig( SIGKILL, cpqfcHBAdata->worker_thread, 1); down( &sem); cpqfcHBAdata->notify_wt = NULL; } // free Linux resources DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n")); free_irq( HostAdapter->irq, HostAdapter); scsi_unregister( HostAdapter); release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff); release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff); /* we get "vfree: bad address" executing this - need to investigate... if( (void*)((unsigned long)cpqfcHBAdata->fcChip.Registers.MemBase) != cpqfcHBAdata->fcChip.Registers.ReMapMemBase) vfree( cpqfcHBAdata->fcChip.Registers.ReMapMemBase);*/ LEAVE("cpqfcTS_release"); return 0;}const char * cpqfcTS_info(struct Scsi_Host *HostAdapter){ static char buf[300]; CPQFCHBA *cpqfcHBA; int BusSpeed, BusWidth; // get the pointer to our Scsi layer HBA buffer cpqfcHBA = (CPQFCHBA *)HostAdapter->hostdata; BusWidth = (cpqfcHBA->fcChip.Registers.PCIMCTR &0x4) > 0 ? 64 : 32; if( cpqfcHBA->fcChip.Registers.TYconfig.value & 0x80000000) BusSpeed = 66; else BusSpeed = 33; sprintf(buf, "%s: WWN %08X%08X\n on PCI bus %d device 0x%02x irq %d IObaseL 0x%x, MEMBASE 0x%x\nPCI bus width %d bits, bus speed %d MHz\nFCP-SCSI Driver v%d.%d.%d", cpqfcHBA->fcChip.Name, cpqfcHBA->fcChip.Registers.wwn_hi, cpqfcHBA->fcChip.Registers.wwn_lo, cpqfcHBA->PciDev->bus->number, cpqfcHBA->PciDev->device, HostAdapter->irq, cpqfcHBA->fcChip.Registers.IOBaseL, cpqfcHBA->fcChip.Registers.MemBase, BusWidth, BusSpeed, VER_MAJOR, VER_MINOR, VER_SUBMINOR); cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); return buf;}//// /proc/scsi support. The following routines allow us to do 'normal'// sprintf like calls to return the currently requested piece (buflenght// chars, starting at bufoffset) of the file. Although procfs allows for// a 1 Kb bytes overflow after te supplied buffer, I consider it bad // programming to use it to make programming a little simpler. This piece// of coding is borrowed from ncr53c8xx.c with some modifications //struct info_str{ char *buffer; // Pointer to output buffer int buflength; // It's length int bufoffset; // File offset corresponding with buf[0] int buffillen; // Current filled length int filpos; // Current file offset};static void copy_mem_info(struct info_str *info, char *data, int datalen){ if (info->filpos < info->bufoffset) { // Current offset before buffer offset if (info->filpos + datalen <= info->bufoffset) { info->filpos += datalen; // Discard if completely before buffer return; } else { // Partial copy, set to begin data += (info->bufoffset - info->filpos); datalen -= (info->bufoffset - info->filpos); info->filpos = info->bufoffset; } } info->filpos += datalen; // Update current offset if (info->buffillen == info->buflength) // Buffer full, discard return; if (info->buflength - info->buffillen < datalen) // Overflows buffer ? datalen = info->buflength - info->buffillen; memcpy(info->buffer + info->buffillen, data, datalen); info->buffillen += datalen;}static int copy_info(struct info_str *info, char *fmt, ...){ va_list args; char buf[400]; int len; va_start(args, fmt); len = vsprintf(buf, fmt, args); va_end(args); copy_mem_info(info, buf, len); return len;}// Routine to get data for /proc RAM filesystem//int cpqfcTS_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout){ struct Scsi_Host *host; Scsi_Cmnd DumCmnd; int Chan, Targ, i; struct info_str info; CPQFCHBA *cpqfcHBA; PTACHYON fcChip; PFC_LOGGEDIN_PORT pLoggedInPort; char buf[81]; // Search the Scsi host list for our controller for (host=scsi_hostlist; host; host=host->next) if (host->host_no == hostno) break; if (!host) return -ESRCH; if (inout) return -EINVAL; // get the pointer to our Scsi layer HBA buffer cpqfcHBA = (CPQFCHBA *)host->hostdata; fcChip = &cpqfcHBA->fcChip; *start = buffer; info.buffer = buffer; info.buflength = length; info.bufoffset = offset; info.filpos = 0; info.buffillen = 0; copy_info(&info, "Driver version = %d.%d.%d", VER_MAJOR, VER_MINOR, VER_SUBMINOR); cpqfcTSDecodeGBICtype( &cpqfcHBA->fcChip, &buf[0]); cpqfcTSGetLPSM( &cpqfcHBA->fcChip, &buf[ strlen(buf)]); copy_info(&info, "%s\n", buf); #define DISPLAY_WWN_INFO#ifdef DISPLAY_WWN_INFO copy_info(&info, "WWN database: (\"port_id: 000000\" means disconnected)\n"); for ( Chan=0; Chan <= host->max_channel; Chan++) { DumCmnd.channel = Chan; for (Targ=0; Targ <= host->max_id; Targ++) { DumCmnd.target = Targ; if ((pLoggedInPort = fcFindLoggedInPort( fcChip, &DumCmnd, // search Scsi Nexus 0, // DON'T search list for FC port id NULL, // DON'T search list for FC WWN NULL))){ // DON'T care about end of list copy_info(&info, "Host: scsi%d Channel: %02d TargetId: %02d -> WWN: ", hostno, Chan, Targ); for( i=3; i>=0; i--) // copy the LOGIN port's WWN copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); for( i=7; i>3; i--) // copy the LOGIN port's WWN copy_info(&info, "%02X", pLoggedInPort->u.ucWWN[i]); copy_info(&info, " port_id: %06X\n", pLoggedInPort->port_id); } } }#endif // Unfortunately, the proc_info buffer isn't big enough// for everything we would like...// For FC stats, compile this and turn off WWN stuff above //#define DISPLAY_FC_STATS#ifdef DISPLAY_FC_STATS// get the Fibre Channel statistics { int DeltaSecs = (jiffies - cpqfcHBA->fcStatsTime) / HZ; int days,hours,minutes,secs; days = DeltaSecs / (3600*24); // days hours = (DeltaSecs% (3600*24)) / 3600; // hours minutes = (DeltaSecs%3600 /60); // minutes secs = DeltaSecs%60; // secscopy_info( &info, "Fibre Channel Stats (time dd:hh:mm:ss %02u:%02u:%02u:%02u\n", days, hours, minutes, secs); } cpqfcHBA->fcStatsTime = jiffies; // (for next delta) copy_info( &info, " LinkUp %9u LinkDown %u\n", fcChip->fcStats.linkUp, fcChip->fcStats.linkDown); copy_info( &info, " Loss of Signal %9u Loss of Sync %u\n", fcChip->fcStats.LossofSignal, fcChip->fcStats.LossofSync); copy_info( &info, " Discarded Frames %9u Bad CRC Frame %u\n", fcChip->fcStats.Dis_Frm, fcChip->fcStats.Bad_CRC); copy_info( &info, " TACH LinkFailTX %9u TACH LinkFailRX %u\n", fcChip->fcStats.linkFailTX, fcChip->fcStats.linkFailRX);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -