📄 3w-xxxx.c
字号:
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, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); /* Now poll for completion */ if (tw_poll_status_gone(tw_dev, TW_STATUS_RESPONSE_QUEUE_EMPTY, 30) == 0) { response_queue.value = inl(TW_RESPONSE_QUEUE_REG_ADDR(tw_dev)); request_id = TW_RESID_OUT(response_queue.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 = ARRAY_SIZE(tw_aen_string); 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 allocate memory */static 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 handles ioctl for the character device */static int tw_chrdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int 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 (mutex_lock_interruptible(&tw_dev->ioctl_lock)) return -EINTR; /* First copy down the buffer length */ if (copy_from_user(&data_buffer_length, argp, sizeof(unsigned int))) goto out; /* Check size */ if (data_buffer_length > TW_MAX_IOCTL_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 = dma_alloc_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, &dma_handle, GFP_KERNEL); if (cpu_addr == NULL) { retval = -ENOMEM; goto out; } tw_ioctl = (TW_New_Ioctl *)cpu_addr; /* Now copy down the entire ioctl */ if (copy_from_user(tw_ioctl, argp, data_buffer_length + sizeof(TW_New_Ioctl) - 1)) 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->host->host_lock, flags); tw_state_request_start(tw_dev, &request_id); /* Flag internal command */ tw_dev->srb[request_id] = NULL; /* Flag chrdev ioctl */ tw_dev->chrdev_request_id = request_id; tw_ioctl->firmware_command.request_id = request_id; /* Load the sg list */ switch (TW_SGL_OUT(tw_ioctl->firmware_command.opcode__sgloffset)) { case 2: tw_ioctl->firmware_command.byte8.param.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1; tw_ioctl->firmware_command.byte8.param.sgl[0].length = data_buffer_length_adjusted; break; case 3: tw_ioctl->firmware_command.byte8.io.sgl[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1; tw_ioctl->firmware_command.byte8.io.sgl[0].length = data_buffer_length_adjusted; break; case 5: passthru->sg_list[0].address = dma_handle + sizeof(TW_New_Ioctl) - 1; passthru->sg_list[0].length = data_buffer_length_adjusted; break; } memcpy(tw_dev->command_packet_virtual_address[request_id], &(tw_ioctl->firmware_command), sizeof(TW_Command)); /* Now post the command packet to the controller */ tw_post_command_packet(tw_dev, request_id); spin_unlock_irqrestore(tw_dev->host->host_lock, flags); timeout = TW_IOCTL_CHRDEV_TIMEOUT*HZ; /* Now wait for the command to complete */ timeout = wait_event_timeout(tw_dev->ioctl_wqueue, tw_dev->chrdev_request_id == TW_IOCTL_CHRDEV_FREE, timeout); /* We timed out, and didn't get an interrupt */ if (tw_dev->chrdev_request_id != TW_IOCTL_CHRDEV_FREE) { /* Now we need to reset the board */ printk(KERN_WARNING "3w-xxxx: scsi%d: Character ioctl (0x%x) timed out, resetting card.\n", tw_dev->host->host_no, cmd); retval = -EIO; if (tw_reset_device_extension(tw_dev)) { printk(KERN_WARNING "3w-xxxx: tw_chrdev_ioctl(): Reset failed for card %d.\n", tw_dev->host->host_no); } goto out2; } /* Now copy in the command packet response */ memcpy(&(tw_ioctl->firmware_command), tw_dev->command_packet_virtual_address[request_id], sizeof(TW_Command)); /* Now complete the io */ spin_lock_irqsave(tw_dev->host->host_lock, flags); tw_dev->posted_request_count--; tw_dev->state[request_id] = TW_S_COMPLETED; tw_state_request_finish(tw_dev, request_id); spin_unlock_irqrestore(tw_dev->host->host_lock, flags); break; default: retval = -ENOTTY; goto out2; } /* Now copy the response to userspace */ if (copy_to_user(argp, tw_ioctl, sizeof(TW_New_Ioctl) + data_buffer_length - 1)) goto out2; retval = 0;out2: /* Now free ioctl buf memory */ dma_free_coherent(&tw_dev->tw_pci_dev->dev, data_buffer_length_adjusted+sizeof(TW_New_Ioctl) - 1, cpu_addr, dma_handle);out: mutex_unlock(&tw_dev->ioctl_lock); return retval;} /* End tw_chrdev_ioctl() *//* This function handles open for the character device */static int tw_chrdev_open(struct inode *inode, struct file *file){ unsigned int minor_number; dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n"); minor_number = iminor(inode); if (minor_number >= tw_device_extension_count) return -ENODEV; return 0;} /* End tw_chrdev_open() *//* File operations struct for character device */static const struct file_operations tw_fops = { .owner = THIS_MODULE, .ioctl = tw_chrdev_ioctl, .open = tw_chrdev_open, .release = NULL};/* This function will free up device extension resources */static void tw_free_device_extension(TW_Device_Extension *tw_dev){ dprintk(KERN_NOTICE "3w-xxxx: tw_free_device_extension()\n"); /* Free command packet and generic buffer memory */ if (tw_dev->command_packet_virtual_address[0]) pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Command)*TW_Q_LENGTH, tw_dev->command_packet_virtual_address[0], tw_dev->command_packet_physical_address[0]); if (tw_dev->alignment_virtual_address[0]) pci_free_consistent(tw_dev->tw_pci_dev, sizeof(TW_Sector)*TW_Q_LENGTH, tw_dev->alignment_virtual_address[0], tw_dev->alignment_physical_address[0]);} /* End tw_free_device_extension() *//* This function will send an initconnection command to controller */static int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits) { unsigned long command_que_value; TW_Command *command_packet; TW_Response_Queue response_queue; int request_id = 0; dprintk(KERN_NOTICE "3w-xxxx: tw_initconnection()\n"); /* Initialize InitConnection command packet */ if (tw_dev->command_packet_virtual_address[request_id] == NULL) { printk(KERN_WARNING "3w-xxxx: tw_initconnection(): 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->opcode__sgloffset = TW_OPSGL_IN(0, TW_OP_INIT_CONNECTION); command_packet->size = TW_INIT_COMMAND_PACKET_SIZE; command_packet->request_id = request_id; command_packet->status = 0x0; command_packet->flags = 0x0; command_packet->byte6.message_credits = message_credits; command_packet->byte8.init_connection.response_queue_pointer = 0x0; command_que_value = tw_dev->command_packet_physical_address[request_id]; if (command_que_value == 0) { printk(KERN_WARNING "3w-xxxx: tw_initconnection(): Bad command packet physical address.\n"); return 1; } /* Send command packet to the board */ outl(command_que_value, TW_COMMAND_QUEUE_REG_ADDR(tw_dev)); /* Poll for completion */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -