📄 file_storage.c
字号:
#endif value = populate_config_buf(fsg->gadget->speed, req->buf, ctrl->wValue >> 8, ctrl->wValue & 0xff); if (value >= 0) value = min(ctrl->wLength, (u16) value); break; case USB_DT_STRING: VDBG(fsg, "get string descriptor\n"); /* wIndex == language code */ value = usb_gadget_get_string(&stringtab, ctrl->wValue & 0xff, req->buf); if (value >= 0) value = min(ctrl->wLength, (u16) value); 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 (ctrl->wValue == CONFIG_VALUE || ctrl->wValue == 0) { fsg->new_config = ctrl->wValue; /* 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 = min(ctrl->wLength, (u16) 1); break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | USB_RECIP_INTERFACE)) break; if (fsg->config && ctrl->wIndex == 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 (ctrl->wIndex != 0) { value = -EDOM; break; } VDBG(fsg, "get interface\n"); *(u8 *) req->buf = 0; value = min(ctrl->wLength, (u16) 1); break; default: VDBG(fsg, "unknown control req %02x.%02x v%04x i%04x l%u\n", ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, 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; ++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) { fsg->ep0req->length = rc; 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, volatile int *pbusy, volatile 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); *pbusy = 1; *state = BUF_STATE_BUSY; 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; /* Wait until a signal arrives or we are woken up */ rc = wait_event_interruptible(fsg->thread_wqh, fsg->thread_wakeup_needed); fsg->thread_wakeup_needed = 0; return (rc ? -EINTR : 0);}/*-------------------------------------------------------------------------*/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) { if ((rc = sleep_thread(fsg)) != 0) 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; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; break; } /* Perform the read */ file_offset_tmp = file_offset; nread = curlun->filp->f_op->read(curlun->filp, (char *) 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; 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; 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; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; continue; } /* Write the received data to the backing file */ bh = fsg->next_buffhd_to_drain; if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; // We stopped early if (bh->state == BUF_STATE_FULL) { fsg->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; curlun->sense_data_info = file_offset >> 9; break; } amount = bh->outreq->actual; if (curlun->file_length - file_offset < amount) { LERROR(curlun, "write %u @ %llu beyond end %llu\n", amount, (unsigned long long) file_offset, (unsigned long long) curlun->file_length); amount = curlun->file_length - file_offset; } /* Perform the write */ file_offset_tmp = file_offset; nwritten = curlun->filp->f_op->write(curlun->filp, (char *) bh->buf, amount, &file_offset_tmp); VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, (unsigned long long) file_offset, (int) nwritten); if (signal_pending(current)) return -EINTR; // Interrupted! if (nwritten < 0) { LDBG(curlun, "error in file write: %d\n", (int) nwritten); nwritten = 0; } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); nwritten -= (nwritten & 511); // Round down to a block } file_offset += nwritten; amount_left_to_write -= nwritten; fsg->residue -= nwritten; /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; curlun->sense_data_info = file_offset >> 9; break; } /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { fsg->short_packet_received = 1; break; } continue; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -