aic7xxx_osm.c

来自「linux 内核源代码」· C语言 代码 · 共 2,346 行 · 第 1/5 页

C
2,346
字号
			tok_end = end;			for (i = 0; tok_list[i]; i++) {				tok_end2 = strchr(opt_arg, tok_list[i]);				if ((tok_end2) && (tok_end2 < tok_end))					tok_end = tok_end2;			}			callback(callback_arg, instance, targ,				 simple_strtol(opt_arg, NULL, 0));			opt_arg = tok_end;			break;		}	}	return (opt_arg);}/* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. * ie. aic7xxx=stpwlev:1,extended */static intaic7xxx_setup(char *s){	int	i, n;	char   *p;	char   *end;	static struct {		const char *name;		uint32_t *flag;	} options[] = {		{ "extended", &aic7xxx_extended },		{ "no_reset", &aic7xxx_no_reset },		{ "verbose", &aic7xxx_verbose },		{ "allow_memio", &aic7xxx_allow_memio},#ifdef AHC_DEBUG		{ "debug", &ahc_debug },#endif		{ "periodic_otag", &aic7xxx_periodic_otag },		{ "pci_parity", &aic7xxx_pci_parity },		{ "seltime", &aic7xxx_seltime },		{ "tag_info", NULL },		{ "global_tag_depth", NULL },		{ "dv", NULL }	};	end = strchr(s, '\0');	/*	 * XXX ia64 gcc isn't smart enough to know that ARRAY_SIZE	 * will never be 0 in this case.	 */	n = 0;	while ((p = strsep(&s, ",.")) != NULL) {		if (*p == '\0')			continue;		for (i = 0; i < ARRAY_SIZE(options); i++) {			n = strlen(options[i].name);			if (strncmp(options[i].name, p, n) == 0)				break;		}		if (i == ARRAY_SIZE(options))			continue;		if (strncmp(p, "global_tag_depth", n) == 0) {			ahc_linux_setup_tag_info_global(p + n);		} else if (strncmp(p, "tag_info", n) == 0) {			s = ahc_parse_brace_option("tag_info", p + n, end,			    2, ahc_linux_setup_tag_info, 0);		} else if (p[n] == ':') {			*(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);		} else if (strncmp(p, "verbose", n) == 0) {			*(options[i].flag) = 1;		} else {			*(options[i].flag) ^= 0xFFFFFFFF;		}	}	return 1;}__setup("aic7xxx=", aic7xxx_setup);uint32_t aic7xxx_verbose;intahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *template){	char	buf[80];	struct	Scsi_Host *host;	char	*new_name;	u_long	s;	int	retval;	template->name = ahc->description;	host = scsi_host_alloc(template, sizeof(struct ahc_softc *));	if (host == NULL)		return (ENOMEM);	*((struct ahc_softc **)host->hostdata) = ahc;	ahc->platform_data->host = host;	host->can_queue = AHC_MAX_QUEUE;	host->cmd_per_lun = 2;	/* XXX No way to communicate the ID for multiple channels */	host->this_id = ahc->our_id;	host->irq = ahc->platform_data->irq;	host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;	host->max_lun = AHC_NUM_LUNS;	host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;	host->sg_tablesize = AHC_NSEG;	ahc_lock(ahc, &s);	ahc_set_unit(ahc, ahc_linux_unit++);	ahc_unlock(ahc, &s);	sprintf(buf, "scsi%d", host->host_no);	new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);	if (new_name != NULL) {		strcpy(new_name, buf);		ahc_set_name(ahc, new_name);	}	host->unique_id = ahc->unit;	ahc_linux_initialize_scsi_bus(ahc);	ahc_intr_enable(ahc, TRUE);	host->transportt = ahc_linux_transport_template;	retval = scsi_add_host(host,			(ahc->dev_softc ? &ahc->dev_softc->dev : NULL));	if (retval) {		printk(KERN_WARNING "aic7xxx: scsi_add_host failed\n");		scsi_host_put(host);		return retval;	}	scsi_scan_host(host);	return 0;}/* * Place the SCSI bus into a known state by either resetting it, * or forcing transfer negotiations on the next command to any * target. */voidahc_linux_initialize_scsi_bus(struct ahc_softc *ahc){	int i;	int numtarg;	unsigned long s;	i = 0;	numtarg = 0;	ahc_lock(ahc, &s);	if (aic7xxx_no_reset != 0)		ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);	if ((ahc->flags & AHC_RESET_BUS_A) != 0)		ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);	else		numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;	if ((ahc->features & AHC_TWIN) != 0) {		if ((ahc->flags & AHC_RESET_BUS_B) != 0) {			ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);		} else {			if (numtarg == 0)				i = 8;			numtarg += 8;		}	}	/*	 * Force negotiation to async for all targets that	 * will not see an initial bus reset.	 */	for (; i < numtarg; i++) {		struct ahc_devinfo devinfo;		struct ahc_initiator_tinfo *tinfo;		struct ahc_tmode_tstate *tstate;		u_int our_id;		u_int target_id;		char channel;		channel = 'A';		our_id = ahc->our_id;		target_id = i;		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {			channel = 'B';			our_id = ahc->our_id_b;			target_id = i % 8;		}		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,					    target_id, &tstate);		ahc_compile_devinfo(&devinfo, our_id, target_id,				    CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);		ahc_update_neg_request(ahc, &devinfo, tstate,				       tinfo, AHC_NEG_ALWAYS);	}	ahc_unlock(ahc, &s);	/* Give the bus some time to recover */	if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {		ahc_linux_freeze_simq(ahc);		msleep(AIC7XXX_RESET_DELAY);		ahc_linux_release_simq(ahc);	}}intahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg){	ahc->platform_data =	    malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);	if (ahc->platform_data == NULL)		return (ENOMEM);	memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));	ahc->platform_data->irq = AHC_LINUX_NOIRQ;	ahc_lockinit(ahc);	ahc->seltime = (aic7xxx_seltime & 0x3) << 4;	ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;	if (aic7xxx_pci_parity == 0)		ahc->flags |= AHC_DISABLE_PCI_PERR;	return (0);}voidahc_platform_free(struct ahc_softc *ahc){	struct scsi_target *starget;	int i;	if (ahc->platform_data != NULL) {		/* destroy all of the device and target objects */		for (i = 0; i < AHC_NUM_TARGETS; i++) {			starget = ahc->platform_data->starget[i];			if (starget != NULL) {				ahc->platform_data->starget[i] = NULL; 			} 		}		if (ahc->platform_data->irq != AHC_LINUX_NOIRQ)			free_irq(ahc->platform_data->irq, ahc);		if (ahc->tag == BUS_SPACE_PIO		 && ahc->bsh.ioport != 0)			release_region(ahc->bsh.ioport, 256);		if (ahc->tag == BUS_SPACE_MEMIO		 && ahc->bsh.maddr != NULL) {			iounmap(ahc->bsh.maddr);			release_mem_region(ahc->platform_data->mem_busaddr,					   0x1000);		}		if (ahc->platform_data->host)			scsi_host_put(ahc->platform_data->host);		free(ahc->platform_data, M_DEVBUF);	}}voidahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb){	ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),				SCB_GET_CHANNEL(ahc, scb),				SCB_GET_LUN(scb), SCB_LIST_NULL,				ROLE_UNKNOWN, CAM_REQUEUE_REQ);}voidahc_platform_set_tags(struct ahc_softc *ahc, struct scsi_device *sdev,		      struct ahc_devinfo *devinfo, ahc_queue_alg alg){	struct ahc_linux_device *dev;	int was_queuing;	int now_queuing;	if (sdev == NULL)		return;	dev = scsi_transport_device_data(sdev);	was_queuing = dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED);	switch (alg) {	default:	case AHC_QUEUE_NONE:		now_queuing = 0;		break; 	case AHC_QUEUE_BASIC:		now_queuing = AHC_DEV_Q_BASIC;		break;	case AHC_QUEUE_TAGGED:		now_queuing = AHC_DEV_Q_TAGGED;		break;	}	if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0	 && (was_queuing != now_queuing)	 && (dev->active != 0)) {		dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;		dev->qfrozen++;	}	dev->flags &= ~(AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED|AHC_DEV_PERIODIC_OTAG);	if (now_queuing) {		u_int usertags;		usertags = ahc_linux_user_tagdepth(ahc, devinfo);		if (!was_queuing) {			/*			 * Start out agressively and allow our			 * dynamic queue depth algorithm to take			 * care of the rest.			 */			dev->maxtags = usertags;			dev->openings = dev->maxtags - dev->active;		}		if (dev->maxtags == 0) {			/*			 * Queueing is disabled by the user.			 */			dev->openings = 1;		} else if (alg == AHC_QUEUE_TAGGED) {			dev->flags |= AHC_DEV_Q_TAGGED;			if (aic7xxx_periodic_otag != 0)				dev->flags |= AHC_DEV_PERIODIC_OTAG;		} else			dev->flags |= AHC_DEV_Q_BASIC;	} else {		/* We can only have one opening. */		dev->maxtags = 0;		dev->openings =  1 - dev->active;	}	switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) {	case AHC_DEV_Q_BASIC:		scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);		scsi_activate_tcq(sdev, dev->openings + dev->active);		break;	case AHC_DEV_Q_TAGGED:		scsi_set_tag_type(sdev, MSG_ORDERED_TAG);		scsi_activate_tcq(sdev, dev->openings + dev->active);		break;	default:		/*		 * We allow the OS to queue 2 untagged transactions to		 * us at any time even though we can only execute them		 * serially on the controller/device.  This should		 * remove some latency.		 */		scsi_deactivate_tcq(sdev, 2);		break;	}}intahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,			int lun, u_int tag, role_t role, uint32_t status){	return 0;}static u_intahc_linux_user_tagdepth(struct ahc_softc *ahc, struct ahc_devinfo *devinfo){	static int warned_user;	u_int tags;	tags = 0;	if ((ahc->user_discenable & devinfo->target_mask) != 0) {		if (ahc->unit >= ARRAY_SIZE(aic7xxx_tag_info)) {			if (warned_user == 0) {				printf(KERN_WARNING"aic7xxx: WARNING: Insufficient tag_info instances\n""aic7xxx: for installed controllers. Using defaults\n""aic7xxx: Please update the aic7xxx_tag_info array in\n""aic7xxx: the aic7xxx_osm..c source file.\n");				warned_user++;			}			tags = AHC_MAX_QUEUE;		} else {			adapter_tag_info_t *tag_info;			tag_info = &aic7xxx_tag_info[ahc->unit];			tags = tag_info->tag_commands[devinfo->target_offset];			if (tags > AHC_MAX_QUEUE)				tags = AHC_MAX_QUEUE;		}	}	return (tags);}/* * Determines the queue depth for a given device. */static voidahc_linux_device_queue_depth(struct scsi_device *sdev){	struct	ahc_devinfo devinfo;	u_int	tags;	struct ahc_softc *ahc = *((struct ahc_softc **)sdev->host->hostdata);	ahc_compile_devinfo(&devinfo,			    sdev->sdev_target->channel == 0			  ? ahc->our_id : ahc->our_id_b,			    sdev->sdev_target->id, sdev->lun,			    sdev->sdev_target->channel == 0 ? 'A' : 'B',			    ROLE_INITIATOR);	tags = ahc_linux_user_tagdepth(ahc, &devinfo);	if (tags != 0 && sdev->tagged_supported != 0) {		ahc_platform_set_tags(ahc, sdev, &devinfo, AHC_QUEUE_TAGGED);		ahc_send_async(ahc, devinfo.channel, devinfo.target,			       devinfo.lun, AC_TRANSFER_NEG);		ahc_print_devinfo(ahc, &devinfo);		printf("Tagged Queuing enabled.  Depth %d\n", tags);	} else {		ahc_platform_set_tags(ahc, sdev, &devinfo, AHC_QUEUE_NONE);		ahc_send_async(ahc, devinfo.channel, devinfo.target,			       devinfo.lun, AC_TRANSFER_NEG);	}}static intahc_linux_run_command(struct ahc_softc *ahc, struct ahc_linux_device *dev,		      struct scsi_cmnd *cmd){	struct	 scb *scb;	struct	 hardware_scb *hscb;	struct	 ahc_initiator_tinfo *tinfo;	struct	 ahc_tmode_tstate *tstate;	uint16_t mask;	struct scb_tailq *untagged_q = NULL;	int nseg;	/*	 * Schedule us to run later.  The only reason we are not	 * running is because the whole controller Q is frozen.	 */	if (ahc->platform_data->qfrozen != 0)		return SCSI_MLQUEUE_HOST_BUSY;	/*	 * We only allow one untagged transaction	 * per target in the initiator role unless	 * we are storing a full busy target *lun*	 * table in SCB space.	 */	if (!blk_rq_tagged(cmd->request)	    && (ahc->features & AHC_SCB_BTT) == 0) {		int target_offset;		target_offset = cmd->device->id + cmd->device->channel * 8;		untagged_q = &(ahc->untagged_queues[target_offset]);		if (!TAILQ_EMPTY(untagged_q))			/* if we're already executing an untagged command			 * we're busy to another */			return SCSI_MLQUEUE_DEVICE_BUSY;	}	/*	 * Get an scb to use.	 */	scb = ahc_get_scb(ahc);	if (!scb)		return SCSI_MLQUEUE_HOST_BUSY;	scb->io_ctx = cmd;	scb->platform_data->dev = dev;

⌨️ 快捷键说明

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