📄 file_storage.c
字号:
/*-------------------------------------------------------------------------*//* Ep0 standard request handlers. These always run in_irq. */static int standard_setup_req(struct fsg_dev *fsg, const struct usb_ctrlrequest *ctrl){ struct usb_request *req = fsg->ep0req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); /* Usually this just stores reply data in the pre-allocated ep0 buffer, * but config change events will also reconfigure hardware. */ switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; switch (w_value >> 8) { case USB_DT_DEVICE: VDBG(fsg, "get device descriptor\n"); value = sizeof device_desc; memcpy(req->buf, &device_desc, value); break; case USB_DT_DEVICE_QUALIFIER: VDBG(fsg, "get device qualifier\n"); if (!gadget_is_dualspeed(fsg->gadget)) break; value = sizeof dev_qualifier; memcpy(req->buf, &dev_qualifier, value); break; case USB_DT_OTHER_SPEED_CONFIG: VDBG(fsg, "get other-speed config descriptor\n"); if (!gadget_is_dualspeed(fsg->gadget)) break; goto get_config; case USB_DT_CONFIG: VDBG(fsg, "get configuration descriptor\n");get_config: value = populate_config_buf(fsg->gadget, req->buf, w_value >> 8, w_value & 0xff); break; case USB_DT_STRING: VDBG(fsg, "get string descriptor\n"); /* wIndex == language code */ value = usb_gadget_get_string(&stringtab, w_value & 0xff, req->buf); break; } break; /* One config, two speeds */ case USB_REQ_SET_CONFIGURATION: if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; VDBG(fsg, "set configuration\n"); if (w_value == CONFIG_VALUE || w_value == 0) { fsg->new_config = w_value; /* Raise an exception to wipe out previous transaction * state (queued bufs, etc) and set the new config. */ raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); value = DELAYED_STATUS; } break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; VDBG(fsg, "get configuration\n"); *(u8 *) req->buf = fsg->config; value = 1; break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | USB_RECIP_INTERFACE)) break; if (fsg->config && w_index == 0) { /* Raise an exception to wipe out previous transaction * state (queued bufs, etc) and install the new * interface altsetting. */ raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); value = DELAYED_STATUS; } break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE)) break; if (!fsg->config) break; if (w_index != 0) { value = -EDOM; break; } VDBG(fsg, "get interface\n"); *(u8 *) req->buf = 0; value = 1; break; default: VDBG(fsg, "unknown control req %02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, le16_to_cpu(ctrl->wLength)); } return value;}static int fsg_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl){ struct fsg_dev *fsg = get_gadget_data(gadget); int rc; int w_length = le16_to_cpu(ctrl->wLength); ++fsg->ep0_req_tag; // Record arrival of a new request fsg->ep0req->context = NULL; fsg->ep0req->length = 0; dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) rc = class_setup_req(fsg, ctrl); else rc = standard_setup_req(fsg, ctrl); /* Respond with data/status or defer until later? */ if (rc >= 0 && rc != DELAYED_STATUS) { rc = min(rc, w_length); fsg->ep0req->length = rc; fsg->ep0req->zero = rc < w_length; fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? "ep0-in" : "ep0-out"); rc = ep0_queue(fsg); } /* Device either stalls (rc < 0) or reports success */ return rc;}/*-------------------------------------------------------------------------*//* All the following routines run in process context *//* Use this for bulk or interrupt transfers, not ep0 */static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, struct usb_request *req, int *pbusy, enum fsg_buffer_state *state){ int rc; if (ep == fsg->bulk_in) dump_msg(fsg, "bulk-in", req->buf, req->length); else if (ep == fsg->intr_in) dump_msg(fsg, "intr-in", req->buf, req->length); spin_lock_irq(&fsg->lock); *pbusy = 1; *state = BUF_STATE_BUSY; spin_unlock_irq(&fsg->lock); rc = usb_ep_queue(ep, req, GFP_KERNEL); if (rc != 0) { *pbusy = 0; *state = BUF_STATE_EMPTY; /* We can't do much more than wait for a reset */ /* Note: currently the net2280 driver fails zero-length * submissions if DMA is enabled. */ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && req->length == 0)) WARN(fsg, "error in submission: %s --> %d\n", ep->name, rc); }}static int sleep_thread(struct fsg_dev *fsg){ int rc = 0; /* Wait until a signal arrives or we are woken up */ for (;;) { try_to_freeze(); set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) { rc = -EINTR; break; } if (fsg->thread_wakeup_needed) break; schedule(); } __set_current_state(TASK_RUNNING); fsg->thread_wakeup_needed = 0; return rc;}/*-------------------------------------------------------------------------*/static int do_read(struct fsg_dev *fsg){ struct lun *curlun = fsg->curlun; u32 lba; struct fsg_buffhd *bh; int rc; u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; unsigned int partial_page; ssize_t nread; /* Get the starting Logical Block Address and check that it's * not too big */ if (fsg->cmnd[0] == SC_READ_6) lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); else { lba = get_be32(&fsg->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = don't read from the * cache), but we don't implement them. */ if ((fsg->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } } if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } file_offset = ((loff_t) lba) << 9; /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; if (unlikely(amount_left == 0)) return -EIO; // No default reply for (;;) { /* Figure out how much we need to read: * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. * Finally, if we're not at a page boundary, don't read past * the next page. * If this means reading 0 then we were asked to read past * the end of file. */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); partial_page = file_offset & (PAGE_CACHE_SIZE - 1); if (partial_page > 0) amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - partial_page); /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) return rc; } /* If we were asked to read past the end of file, * end with an empty buffer. */ if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; curlun->sense_data_info = file_offset >> 9; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; break; } /* Perform the read */ file_offset_tmp = file_offset; nread = vfs_read(curlun->filp, (char __user *) bh->buf, amount, &file_offset_tmp); VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nread); if (signal_pending(current)) return -EINTR; if (nread < 0) { LDBG(curlun, "error in file read: %d\n", (int) nread); nread = 0; } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int) nread, amount); nread -= (nread & 511); // Round down to a block } file_offset += nread; amount_left -= nread; fsg->residue -= nread; bh->inreq->length = nread; bh->state = BUF_STATE_FULL; /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; curlun->sense_data_info = file_offset >> 9; curlun->info_valid = 1; break; } if (amount_left == 0) break; // No more left to read /* Send this buffer and go read some more */ bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; } return -EIO; // No default reply}/*-------------------------------------------------------------------------*/static int do_write(struct fsg_dev *fsg){ struct lun *curlun = fsg->curlun; u32 lba; struct fsg_buffhd *bh; int get_some_more; u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; unsigned int partial_page; ssize_t nwritten; int rc; if (curlun->ro) { curlun->sense_data = SS_WRITE_PROTECTED; return -EINVAL; } curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait /* Get the starting Logical Block Address and check that it's * not too big */ if (fsg->cmnd[0] == SC_WRITE_6) lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); else { lba = get_be32(&fsg->cmnd[2]); /* We allow DPO (Disable Page Out = don't save data in the * cache) and FUA (Force Unit Access = write directly to the * medium). We don't implement DPO; we implement FUA by * performing synchronous output. */ if ((fsg->cmnd[1] & ~0x18) != 0) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } if (fsg->cmnd[1] & 0x08) // FUA curlun->filp->f_flags |= O_SYNC; } if (lba >= curlun->num_sectors) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } /* Carry out the file writes */ get_some_more = 1; file_offset = usb_offset = ((loff_t) lba) << 9; amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; while (amount_left_to_write > 0) { /* Queue a request for more data from the host */ bh = fsg->next_buffhd_to_fill; if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: * Try to get the remaining amount. * But don't get more than the buffer size. * And don't try to go past the end of the file. * If we're not at a page boundary, * don't go past the next page. * If this means getting 0, then we were asked * to write past the end of file. * Finally, round down to a block boundary. */ amount = min(amount_left_to_req, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - usb_offset); partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); if (partial_page > 0) amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - partial_page); if (amount == 0) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; curlun->sense_data_info = usb_offset >> 9; curlun->info_valid = 1; continue; } amount -= (amount & 511); if (amount == 0) { /* Why were we were asked to transfer a * partial block? */ get_some_more = 0; continue; } /* Get the next buffer */ usb_offset += amount; fsg->usb_amount_left -= amount; amount_left_to_req -= amount; if (amount_left_to_req == 0) get_some_more = 0; /* amount is always divisible by 512, hence by * the bulk-out maxpacket size */ bh->outreq->length = bh->bulk_out_intended_length = amount;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -