?? 3w-xxxx.c
字號:
printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet virtual address.\n"); return 1; } command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; memset(command_packet, 0, sizeof(TW_Sector)); command_packet->byte0.opcode = TW_OP_GET_PARAM; command_packet->byte0.sgl_offset = 2; command_packet->size = 4; command_packet->request_id = request_id; command_packet->byte3.unit = 0; command_packet->byte3.host_id = 0; command_packet->status = 0; command_packet->flags = 0; command_packet->byte6.parameter_count = 1; command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad command packet physical address.\n"); return 1; } /* Now setup the param */ if (tw_dev->alignment_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment virtual address.\n"); return 1; } param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; memset(param, 0, sizeof(TW_Sector)); param->table_id = 0x401; /* AEN table */ param->parameter_id = 2; /* Unit code */ param->parameter_size_bytes = 2; param_value = tw_dev->alignment_physical_address[request_id]; if (param_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Bad alignment physical address.\n"); return 1; } command_packet->byte8.param.sgl[0].address = param_value; command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); /* Now drain the controller's aen queue */ do { /* Post command packet */ outl(command_que_value, command_que_addr); /* Now poll for completion */ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) { response_queue.value = inl(response_que_addr); request_id = (unsigned char)response_queue.u.response_id; if (request_id != 0) { /* Unexpected request id */ printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unexpected request id.\n"); return 1; } if (command_packet->status != 0) { if (command_packet->flags != TW_AEN_TABLE_UNDEFINED) { /* Bad response */ tw_decode_sense(tw_dev, request_id, 0); return 1; } else { /* We know this is a 3w-1x00, and doesn't support aen's */ return 0; } } /* Now check the aen */ aen = *(unsigned short *)(param->data); aen_code = (aen & 0x0ff); queue = 0; switch (aen_code) { case TW_AEN_QUEUE_EMPTY: dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); if (first_reset != 1) { return 1; } else { finished = 1; } break; case TW_AEN_SOFT_RESET: if (first_reset == 0) { first_reset = 1; } else { printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); tw_dev->aen_count++; queue = 1; } break; default: if (aen == 0x0ff) { printk(KERN_WARNING "3w-xxxx: AEN: INFO: AEN queue overflow.\n"); } else { table_max = sizeof(tw_aen_string)/sizeof(char *); if ((aen & 0x0ff) < table_max) { if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') { printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8); } else { printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]); } } else printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen); } tw_dev->aen_count++; queue = 1; } /* Now put the aen on the aen_queue */ if (queue == 1) { tw_dev->aen_queue[tw_dev->aen_tail] = aen; if (tw_dev->aen_tail == TW_Q_LENGTH - 1) { tw_dev->aen_tail = TW_Q_START; } else { tw_dev->aen_tail = tw_dev->aen_tail + 1; } if (tw_dev->aen_head == tw_dev->aen_tail) { if (tw_dev->aen_head == TW_Q_LENGTH - 1) { tw_dev->aen_head = TW_Q_START; } else { tw_dev->aen_head = tw_dev->aen_head + 1; } } } found = 1; } if (found == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Response never received.\n"); return 1; } } while (finished == 0); return 0;} /* End tw_aen_drain_queue() *//* This function will read the aen queue from the isr */int tw_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) { TW_Command *command_packet; TW_Param *param; u32 command_que_addr; unsigned long command_que_value; u32 status_reg_value = 0, status_reg_addr; unsigned long param_value = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_aen_read_queue()\n"); command_que_addr = tw_dev->registers.command_que_addr; status_reg_addr = tw_dev->registers.status_reg_addr; status_reg_value = inl(status_reg_addr); if (tw_check_bits(status_reg_value)) { dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Unexpected bits.\n"); tw_decode_bits(tw_dev, status_reg_value, 1); return 1; } if (tw_dev->command_packet_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet virtual address.\n"); return 1; } command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id]; memset(command_packet, 0, sizeof(TW_Sector)); command_packet->byte0.opcode = TW_OP_GET_PARAM; command_packet->byte0.sgl_offset = 2; command_packet->size = 4; command_packet->request_id = request_id; command_packet->byte3.unit = 0; command_packet->byte3.host_id = 0; command_packet->status = 0; command_packet->flags = 0; command_packet->byte6.parameter_count = 1; command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad command packet physical address.\n"); return 1; } /* Now setup the param */ if (tw_dev->alignment_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment virtual address.\n"); return 1; } param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; memset(param, 0, sizeof(TW_Sector)); param->table_id = 0x401; /* AEN table */ param->parameter_id = 2; /* Unit code */ param->parameter_size_bytes = 2; param_value = tw_dev->alignment_physical_address[request_id]; if (param_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Bad alignment physical address.\n"); return 1; } command_packet->byte8.param.sgl[0].address = param_value; command_packet->byte8.param.sgl[0].length = sizeof(TW_Sector); /* Now post the command packet */ if ((status_reg_value & TW_STATUS_COMMAND_QUEUE_FULL) == 0) { dprintk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post succeeded.\n"); tw_dev->srb[request_id] = NULL; /* Flag internal command */ tw_dev->state[request_id] = TW_S_POSTED; outl(command_que_value, command_que_addr); } else { printk(KERN_WARNING "3w-xxxx: tw_aen_read_queue(): Post failed, will retry.\n"); return 1; } return 0;} /* End tw_aen_read_queue() *//* This function will allocate memory */int tw_allocate_memory(TW_Device_Extension *tw_dev, int size, int which){ int i; dma_addr_t dma_handle; unsigned long *cpu_addr = NULL; dprintk(KERN_NOTICE "3w-xxxx: tw_allocate_memory()\n"); cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, &dma_handle); if (cpu_addr == NULL) { printk(KERN_WARNING "3w-xxxx: pci_alloc_consistent() failed.\n"); return 1; } if ((unsigned long)cpu_addr % (tw_dev->tw_pci_dev->device == TW_DEVICE_ID ? TW_ALIGNMENT_6000 : TW_ALIGNMENT_7000)) { printk(KERN_WARNING "3w-xxxx: Couldn't allocate correctly aligned memory.\n"); pci_free_consistent(tw_dev->tw_pci_dev, size*TW_Q_LENGTH, cpu_addr, dma_handle); return 1; } memset(cpu_addr, 0, size*TW_Q_LENGTH); for (i=0;i<TW_Q_LENGTH;i++) { switch(which) { case 0: tw_dev->command_packet_physical_address[i] = dma_handle+(i*size); tw_dev->command_packet_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); break; case 1: tw_dev->alignment_physical_address[i] = dma_handle+(i*size); tw_dev->alignment_virtual_address[i] = (unsigned long *)((unsigned char *)cpu_addr + (i*size)); break; default: printk(KERN_WARNING "3w-xxxx: tw_allocate_memory(): case slip in tw_allocate_memory()\n"); return 1; } } return 0;} /* End tw_allocate_memory() *//* This function will check the status register for unexpected bits */int tw_check_bits(u32 status_reg_value){ if ((status_reg_value & TW_STATUS_EXPECTED_BITS) != TW_STATUS_EXPECTED_BITS) { dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): No expected bits (0x%x).\n", status_reg_value); return 1; } if ((status_reg_value & TW_STATUS_UNEXPECTED_BITS) != 0) { dprintk(KERN_WARNING "3w-xxxx: tw_check_bits(): Found unexpected bits (0x%x).\n", status_reg_value); return 1; } return 0;} /* End tw_check_bits() *//* This function will report controller error status */int tw_check_errors(TW_Device_Extension *tw_dev) { u32 status_reg_addr, status_reg_value; status_reg_addr = tw_dev->registers.status_reg_addr; status_reg_value = inl(status_reg_addr); if (TW_STATUS_ERRORS(status_reg_value) || tw_check_bits(status_reg_value)) { tw_decode_bits(tw_dev, status_reg_value, 0); return 1; } return 0;} /* End tw_check_errors() *//* This function handles ioctl for the character device */static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int error, request_id; dma_addr_t dma_handle; unsigned short tw_aen_code; unsigned long flags; unsigned int data_buffer_length = 0; unsigned long data_buffer_length_adjusted = 0; unsigned long *cpu_addr; long timeout; TW_New_Ioctl *tw_ioctl; TW_Passthru *passthru; TW_Device_Extension *tw_dev = tw_device_extension_list[iminor(inode)]; int retval = -EFAULT; void __user *argp = (void __user *)arg; dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl()\n"); /* Only let one of these through at a time */ if (down_interruptible(&tw_dev->ioctl_sem)) return -EINTR; /* First copy down the buffer length */ error = copy_from_user(&data_buffer_length, argp, sizeof(unsigned int)); if (error) goto out; /* Check size */ if (data_buffer_length > TW_MAX_SECTORS * 512) { retval = -EINVAL; goto out; } /* Hardware can only do multiple of 512 byte transfers */ data_buffer_length_adjusted = (data_buffer_length + 511) & ~511; /* Now allocate ioctl buf memory */ cpu_addr = pci_alloc_consistent(tw_dev->tw_pci_dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle); if (cpu_addr == NULL) { retval = -ENOMEM; goto out; } tw_ioctl = (TW_New_Ioctl *)cpu_addr; /* Now copy down the entire ioctl */ error = copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl) - 1); if (error) goto out2; passthru = (TW_Passthru *)&tw_ioctl->firmware_command; /* See which ioctl we are doing */ switch (cmd) { case TW_OP_NOP: dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_OP_NOP.\n"); break; case TW_OP_AEN_LISTEN: dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_AEN_LISTEN.\n"); memset(tw_ioctl->data_buffer, 0, data_buffer_length); spin_lock_irqsave(tw_dev->host->host_lock, flags); if (tw_dev->aen_head == tw_dev->aen_tail) { tw_aen_code = TW_AEN_QUEUE_EMPTY; } else { tw_aen_code = tw_dev->aen_queue[tw_dev->aen_head]; if (tw_dev->aen_head == TW_Q_LENGTH - 1) { tw_dev->aen_head = TW_Q_START; } else { tw_dev->aen_head = tw_dev->aen_head + 1; } } spin_unlock_irqrestore(tw_dev->host->host_lock, flags); memcpy(tw_ioctl->data_buffer, &tw_aen_code, sizeof(tw_aen_code)); break; case TW_CMD_PACKET_WITH_DATA: dprintk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n"); spin_lock_irqsave(&tw_dev->tw_lock, flags); tw_state_request_start(tw_dev, &request_id); /* Flag internal command */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -