3w-9xxx.c

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

C
1,986
字号
		/* Handle host interrupt */		if (status_reg_value & TW_STATUS_HOST_INTERRUPT)			TW_CLEAR_HOST_INTERRUPT(tw_dev);		/* Handle attention interrupt */		if (status_reg_value & TW_STATUS_ATTENTION_INTERRUPT) {			TW_CLEAR_ATTENTION_INTERRUPT(tw_dev);			if (!(test_and_set_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags))) {				twa_get_request_id(tw_dev, &request_id);				error = twa_aen_read_queue(tw_dev, request_id);				if (error) {					tw_dev->state[request_id] = TW_S_COMPLETED;					twa_free_request_id(tw_dev, request_id);					clear_bit(TW_IN_ATTENTION_LOOP, &tw_dev->flags);				}			}		}		/* Handle command interrupt */		if (status_reg_value & TW_STATUS_COMMAND_INTERRUPT) {			TW_MASK_COMMAND_INTERRUPT(tw_dev);			/* Drain as many pending commands as we can */			while (tw_dev->pending_request_count > 0) {				request_id = tw_dev->pending_queue[tw_dev->pending_head];				if (tw_dev->state[request_id] != TW_S_PENDING) {					TW_PRINTK(tw_dev->host, TW_DRIVER, 0x19, "Found request id that wasn't pending");					TW_CLEAR_ALL_INTERRUPTS(tw_dev);					goto twa_interrupt_bail;				}				if (twa_post_command_packet(tw_dev, request_id, 1)==0) {					tw_dev->pending_head = (tw_dev->pending_head + 1) % TW_Q_LENGTH;					tw_dev->pending_request_count--;				} else {					/* If we get here, we will continue re-posting on the next command interrupt */					break;				}			}		}		/* Handle response interrupt */		if (status_reg_value & TW_STATUS_RESPONSE_INTERRUPT) {			/* Drain the response queue from the board */			while ((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) {				/* Complete the response */				response_que.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));				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) {						error = twa_fill_sense(tw_dev, request_id, 1, 1);					} else {						/* Skip ioctl error prints */						if (request_id != tw_dev->chrdev_request_id) {							error = twa_fill_sense(tw_dev, request_id, 0, 1);						}					}				}				/* Check for correct state */				if (tw_dev->state[request_id] != TW_S_POSTED) {					if (tw_dev->srb[request_id] != 0) {						TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1a, "Received a request id that wasn't posted");					        TW_CLEAR_ALL_INTERRUPTS(tw_dev);						goto twa_interrupt_bail;					}				}				/* Check for internal command completion */				if (tw_dev->srb[request_id] == 0) {					if (request_id != tw_dev->chrdev_request_id) {						if (twa_aen_complete(tw_dev, request_id))							TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1b, "Error completing AEN during attention interrupt");					} else {						tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;						wake_up(&tw_dev->ioctl_wqueue);					}				} else {					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);					}					/* If error, command failed */					if (error == 1) {						/* Ask for a host reset */						tw_dev->srb[request_id]->result = (DID_OK << 16) | (CHECK_CONDITION << 1);					}					/* Now complete the io */					tw_dev->state[request_id] = TW_S_COMPLETED;					twa_free_request_id(tw_dev, request_id);					tw_dev->posted_request_count--;					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);					twa_unmap_scsi_data(tw_dev, request_id);				}				/* Check for valid status after each drain */				status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));				if (twa_check_bits(status_reg_value)) {					if (twa_decode_bits(tw_dev, status_reg_value)) {						TW_CLEAR_ALL_INTERRUPTS(tw_dev);						goto twa_interrupt_bail;					}				}			}		}	}twa_interrupt_bail:	spin_unlock(tw_dev->host->host_lock);	return IRQ_RETVAL(handled);} /* 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){	TW_Command *oldcommand;	TW_Command_Apache *newcommand;	TW_SG_Entry *sgl;	if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {		newcommand = &full_command_packet->command.newcommand;		newcommand->request_id = request_id;		newcommand->sg_list[0].address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;		newcommand->sg_list[0].length = length;	} else {		oldcommand = &full_command_packet->command.oldcommand;		oldcommand->request_id = request_id;		if (TW_SGL_OUT(oldcommand->opcode__sgloffset)) {			/* Load the sg list */			sgl = (TW_SG_Entry *)((u32 *)oldcommand+TW_SGL_OUT(oldcommand->opcode__sgloffset));			sgl->address = dma_handle + sizeof(TW_Ioctl_Buf_Apache) - 1;			sgl->length = length;		}	}} /* End twa_load_sgl() *//* This function will perform a pci-dma mapping for a scatter gather list */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->buffer, cmd->use_sg, DMA_BIDIRECTIONAL);	if (use_sg == 0) {		TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1c, "Failed to map scatter gather list");		goto out;	}	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;	int 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() *//* 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){	int retval = 1, found = 0, response_request_id;	TW_Response_Queue response_queue;	TW_Command_Full *full_command_packet = tw_dev->command_packet_virt[request_id];	if (twa_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, seconds) == 0) {		response_queue.value = readl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev));		response_request_id = TW_RESID_OUT(response_queue.response_id);		if (request_id != response_request_id) {			TW_PRINTK(tw_dev->host, TW_DRIVER, 0x1e, "Found unexpected request id while polling for response");			goto out;		}		if (TW_OP_OUT(full_command_packet->command.newcommand.opcode__reserved) == TW_OP_EXECUTE_SCSI) {			if (full_command_packet->command.newcommand.status != 0) {				/* bad response */				twa_fill_sense(tw_dev, request_id, 0, 0);				goto out;			}			found = 1;		} else {			if (full_command_packet->command.oldcommand.status != 0) {				/* bad response */				twa_fill_sense(tw_dev, request_id, 0, 0);				goto out;			}			found = 1;		}	}	if (found)		retval = 0;out:	return retval;} /* End twa_poll_response() *//* This function will poll the status register for a flag */static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds){	u32 status_reg_value; 	unsigned long before;	int retval = 1;	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));	before = jiffies;	if (twa_check_bits(status_reg_value))		twa_decode_bits(tw_dev, status_reg_value);	while ((status_reg_value & flag) != flag) {		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));		if (twa_check_bits(status_reg_value))			twa_decode_bits(tw_dev, status_reg_value);		if (time_after(jiffies, before + HZ * seconds))			goto out;		msleep(50);	}	retval = 0;out:	return retval;} /* End twa_poll_status() *//* This function will poll the status register for disappearance of a flag */static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds){	u32 status_reg_value;	unsigned long before;	int retval = 1;	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));	before = jiffies;	if (twa_check_bits(status_reg_value))		twa_decode_bits(tw_dev, status_reg_value);	while ((status_reg_value & flag) != 0) {		status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));		if (twa_check_bits(status_reg_value))			twa_decode_bits(tw_dev, status_reg_value);		if (time_after(jiffies, before + HZ * seconds))			goto out;		msleep(50);	}	retval = 0;out:	return retval;} /* End twa_poll_status_gone() *//* This function will attempt to post a command packet to the board */static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal){	u32 status_reg_value;	unsigned long command_que_value;	int retval = 1;	command_que_value = tw_dev->command_packet_phys[request_id];	status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev));	if (twa_check_bits(status_reg_value))		twa_decode_bits(tw_dev, status_reg_value);	if (((tw_dev->pending_request_count > 0) && (tw_dev->state[request_id] != TW_S_PENDING)) || (status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL)) {		/* Only pend internal driver commands */		if (!internal) {			retval = SCSI_MLQUEUE_HOST_BUSY;			goto out;		}		/* Couldn't post the command packet, so we do it later */		if (tw_dev->state[request_id] != TW_S_PENDING) {			tw_dev->state[request_id] = TW_S_PENDING;			tw_dev->pending_request_count++;			if (tw_dev->pending_request_count > tw_dev->max_pending_request_count) {				tw_dev->max_pending_request_count = tw_dev->pending_request_count;			}			tw_dev->pending_queue[tw_dev->pending_tail] = request_id;			tw_dev->pending_tail = (tw_dev->pending_tail + 1) % TW_Q_LENGTH;		}		TW_UNMASK_COMMAND_INTERRUPT(tw_dev);		goto out;	} else {		/* We successfully posted the command packet */#if BITS_PER_LONG > 32		writeq(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));#else		writel(TW_COMMAND_OFFSET + command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev));#endif		tw_dev->state[request_id] = TW_S_POSTED;		tw_dev->posted_request_count++;		if (tw_dev->posted_request_count > tw_dev->max_posted_request_count) {			tw_dev->max_posted_request_count = tw_dev->posted_request_count;		}	}	retval = 0;out:	return retval;} /* End twa_post_command_packet() *//* This function will reset a device extension */static int twa_reset_device_extension(TW_Device_Extension *tw_dev){	int i = 0;	int retval = 1;	/* Abort all requests that are in progress */	for (i = 0; i < TW_Q_LENGTH; i++) {		if ((tw_dev->state[i] != TW_S_FINISHED) &&		    (tw_dev->state[i] != TW_S_INITIAL) &&		    (tw_dev->state[i] != TW_S_COMPLETED)) {			if (tw_dev->srb[i]) {				tw_dev->srb[i]->result = (DID_RESET << 16);				tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);				twa_unmap_scsi_data(tw_dev, i);			}		}	}	/* Reset queues and counts */	for (i = 0; i < TW_Q_LENGTH; i++) {		tw_dev->free_queue[i] = i;		tw_dev->state[i] = TW_S_INITIAL;	}	tw_dev->free_head = TW_Q_START;	tw_dev->free_tail = TW_Q_START;	tw_dev->posted_request_count = 0;	tw_dev->pending_request_count = 0;	tw_dev->pending_head = TW_Q_START;	tw_dev->pending_tail = TW_Q_START;	tw_dev->reset_print = 0;	tw_dev->chrdev_request_id = TW_IOCTL_CHRDEV_FREE;	tw_dev->flags = 0;	TW_DISABLE_INTERRUPTS(tw_dev);	if (twa_reset_sequence(tw_dev, 1))		goto out;        TW_ENABLE_AND_CLEAR_INTERRUPTS(tw_dev);	retval = 0;out:	return retval;} /* End twa_reset_device_extension() *//* This function will reset a controller */static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset){	int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset;	while (tries < TW_MAX_RESET_TRIES) {		if (do_soft_reset)			TW_SOFT_RESET(tw_dev);		/* Make sure controller is in a good state */		if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 30)) {

⌨️ 快捷键说明

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