cpqfctsinit.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,099 行 · 第 1/5 页

C
2,099
字号
      cpqfcTStimer->data = (unsigned long)cpqfcHBAdata; // this adapter      cpqfcTStimer->function = cpqfcTSheartbeat; // handles timeouts, housekeeping      add_timer( cpqfcTStimer);  // give it to Linux      // now initialize our hardware...      if (cpqfcHBAdata->fcChip.InitializeTachyon( cpqfcHBAdata, 1,1)) {	printk(KERN_WARNING "cpqfc: initialization of HBA hardware failed.\n");	goto err_release_region_L;      }      cpqfcHBAdata->fcStatsTime = jiffies;  // (for FC Statistics delta)            // give our HBA time to initialize and login current devices...      {	// The Brocade switch (e.g. 2400, 2010, etc.) as of March 2000,	// has the following algorithm for FL_Port startup:	// Time(sec) Action	// 0:        Device Plugin and LIP(F7,F7) transmission	// 1.0       LIP incoming        // 1.027     LISA incoming, no CLS! (link not up)	// 1.028     NOS incoming (switch test for N_Port)        // 1.577     ED_TOV expired, transmit LIPs again		// 3.0       LIP(F8,F7) incoming (switch passes Tach Prim.Sig)	// 3.028     LILP received, link up, FLOGI starts	// slowest(worst) case, measured on 1Gb Finisar GT analyzer		unsigned long stop_time;	spin_unlock_irq(HostAdapter->host_lock);	stop_time = jiffies + 4*HZ;        while ( time_before(jiffies, stop_time) ) 	  	schedule();  // (our worker task needs to run)      }            spin_lock_irq(HostAdapter->host_lock);      NumberOfAdapters++;       spin_unlock_irq(HostAdapter->host_lock);      continue;err_release_region_L:      release_region( cpqfcHBAdata->fcChip.Registers.IOBaseL, 0xff );err_release_region_U:      release_region( cpqfcHBAdata->fcChip.Registers.IOBaseU, 0xff );err_free_irq:      free_irq( HostAdapter->irq, HostAdapter);err_unregister:      scsi_unregister( HostAdapter);err_disable_dev:      pci_disable_device( PciDev );err_continue:      continue;    } // end of while()  }  LEAVE("cpqfcTS_detect");   return NumberOfAdapters;}#ifdef SUPPORT_RESETstatic void my_ioctl_done (Scsi_Cmnd * SCpnt){    struct request * req;        req = SCpnt->request;    req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */      if (req->CPQFC_WAITING != NULL)	CPQFC_COMPLETE(req->CPQFC_WAITING);}   #endifstatic int cpqfc_alloc_private_data_pool(CPQFCHBA *hba){	hba->private_data_bits = NULL;	hba->private_data_pool = NULL;	hba->private_data_bits = 		kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /				BITS_PER_LONG)*sizeof(unsigned long), 				GFP_KERNEL);	if (hba->private_data_bits == NULL)		return -1;	memset(hba->private_data_bits, 0,		((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) /				BITS_PER_LONG)*sizeof(unsigned long));	hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) *			CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL);	if (hba->private_data_pool == NULL) {		kfree(hba->private_data_bits);		hba->private_data_bits = NULL;		return -1;	}	return 0;}static void cpqfc_free_private_data_pool(CPQFCHBA *hba){	kfree(hba->private_data_bits);	kfree(hba->private_data_pool);}int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer){	/* Is pointer within our private data pool?	   We use Scsi_Request->upper_private_data (normally	   reserved for upper layer drivers, e.g. the sg driver)	   We check to see if the pointer is ours by looking at	   its address.  Is this ok?   Hmm, it occurs to me that	   a user app might do something bad by using sg to send	   a cpqfc passthrough ioctl with upper_data_private	   forged to be somewhere in our pool..., though they'd	   normally have to be root already to do this.  */	return (pointer != NULL && 		pointer >= (void *) hba->private_data_pool && 		pointer < (void *) hba->private_data_pool + 			sizeof(*hba->private_data_pool) * 				CPQFC_MAX_PASSTHRU_CMDS);}cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba){	int i;	do {		i = find_first_zero_bit(hba->private_data_bits, 			CPQFC_MAX_PASSTHRU_CMDS);		if (i == CPQFC_MAX_PASSTHRU_CMDS)			return NULL;	} while ( test_and_set_bit(i & (BITS_PER_LONG - 1), 			hba->private_data_bits+(i/BITS_PER_LONG)) != 0);	return &hba->private_data_pool[i];}void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data){	int i;	i = data - hba->private_data_pool;	clear_bit(i&(BITS_PER_LONG-1), 			hba->private_data_bits+(i/BITS_PER_LONG));}int cpqfcTS_ioctl( struct scsi_device *ScsiDev, int Cmnd, void *arg){  int result = 0;  struct Scsi_Host *HostAdapter = ScsiDev->host;  CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata;  PTACHYON fcChip = &cpqfcHBAdata->fcChip;  PFC_LOGGEDIN_PORT pLoggedInPort = NULL;  struct scsi_cmnd *DumCmnd;  int i, j;  VENDOR_IOCTL_REQ ioc;  cpqfc_passthru_t *vendor_cmd;  Scsi_Device *SDpnt;  Scsi_Request *ScsiPassThruReq;  cpqfc_passthru_private_t *privatedata;  ENTER("cpqfcTS_ioctl ");    // printk("ioctl CMND %d", Cmnd);    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	/* Check that our pool got allocated ok. */	if (cpqfcHBAdata->private_data_pool == NULL)		return -ENOMEM;		if( !arg)	  return -EINVAL;	// must be super user to send stuff directly to the	// controller and/or physical drives...	if( !capable(CAP_SYS_RAWIO) )	  return -EPERM;	// copy the caller's struct to our space.        if( copy_from_user( &ioc, arg, sizeof( VENDOR_IOCTL_REQ)))		return( -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_Request to pass down...        ScsiPassThruReq = scsi_allocate_request(ScsiDev, GFP_KERNEL);	if (ScsiPassThruReq == NULL) {		kfree(buf);		return -ENOMEM;	}	ScsiPassThruReq->upper_private_data = 			cpqfc_alloc_private_data(cpqfcHBAdata);	if (ScsiPassThruReq->upper_private_data == NULL) {		kfree(buf);		scsi_release_request(ScsiPassThruReq); // "de-allocate"		return -ENOMEM;	}	if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) {		if (vendor_cmd->len) { // Need data from user?        		if (copy_from_user(buf, vendor_cmd->bufp, 						vendor_cmd->len)) {				kfree(buf);				cpqfc_free_private_data(cpqfcHBAdata, 					ScsiPassThruReq->upper_private_data);				scsi_release_request(ScsiPassThruReq);				return( -EFAULT);			}		}		ScsiPassThruReq->sr_data_direction = SCSI_DATA_WRITE; 	} else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) {		ScsiPassThruReq->sr_data_direction = SCSI_DATA_READ; 	} else		// maybe this means a bug in the user app		ScsiPassThruReq->sr_data_direction = SCSI_DATA_NONE;	    	ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req()	ScsiPassThruReq->sr_sense_buffer[0] = 0;	ScsiPassThruReq->sr_sense_buffer[2] = 0;        // We copy the scheme used by sd.c:spinup_disk() 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".	privatedata = ScsiPassThruReq->upper_private_data;	privatedata->bus = vendor_cmd->bus;	privatedata->pdrive = vendor_cmd->pdrive;	        // eventually gets us to our own _quecommand routine	scsi_wait_req(ScsiPassThruReq, 		&vendor_cmd->cdb[0], buf, vendor_cmd->len, 		10*HZ,  // timeout		1);	// retries        result = ScsiPassThruReq->sr_result;        // copy any sense data back to caller        if( result != 0 )	{	  memcpy( vendor_cmd->sense_data, // see struct def - size=40		  ScsiPassThruReq->sr_sense_buffer, 		  sizeof(ScsiPassThruReq->sr_sense_buffer) <                  sizeof(vendor_cmd->sense_data)           ?                  sizeof(ScsiPassThruReq->sr_sense_buffer) :                  sizeof(vendor_cmd->sense_data)                ); 	}        SDpnt = ScsiPassThruReq->sr_device;	/* upper_private_data is already freed in call_scsi_done() */        scsi_release_request(ScsiPassThruReq); // "de-allocate"        ScsiPassThruReq = NULL;	// need to pass data back to user (space)?	if( (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) &&	     vendor_cmd->len )        if(  copy_to_user( vendor_cmd->bufp, buf, vendor_cmd->len))		result = -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); 	              if(copy_to_user( arg, &pciinfo, sizeof(cpqfc_pci_info_struct)))		return( -EFAULT);        return 0;      }      case CPQFCTS_GETDRIVVER:      {	DriverVer_type DriverVer = 		CPQFCTS_DRIVER_VER( VER_MAJOR,VER_MINOR,VER_SUBMINOR);		if( !arg)	  return -EINVAL;        if(copy_to_user( arg, &DriverVer, sizeof(DriverVer)))		return( -EFAULT);        return 0;      }      case CPQFC_IOCTL_FC_TARGET_ADDRESS:	// can we find an FC device mapping to this SCSI target?/* 	DumCmnd.channel = ScsiDev->channel; */		// For searching/* 	DumCmnd.target  = ScsiDev->id; *//* 	DumCmnd.lun     = ScsiDev->lun; */	DumCmnd = scsi_get_command (ScsiDev, GFP_KERNEL);	if (!DumCmnd)		return -ENOMEM;		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	scsi_put_command (DumCmnd);	if (pLoggedInPort == NULL) {		result = -ENXIO;		break;	}	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;      case CPQFC_IOCTL_FC_TDR:                  result = cpqfcTS_TargetDeviceReset( ScsiDev, 0);        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;      }  cpqfc_free_private_data_pool(cpqfcHBAdata);  // 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);*/  pci_disable_device( cpqfcHBAdata->PciDev);  LEAVE("cpqfcTS_release");  return 0;}const char * cpqfcTS_info(struct Scsi_Host *HostAdapter){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?