aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/shost/shost_transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/shost/shost_transfer.c')
-rw-r--r--drivers/usb/host/shost/shost_transfer.c1025
1 files changed, 1025 insertions, 0 deletions
diff --git a/drivers/usb/host/shost/shost_transfer.c b/drivers/usb/host/shost/shost_transfer.c
new file mode 100644
index 0000000..122ce65
--- /dev/null
+++ b/drivers/usb/host/shost/shost_transfer.c
@@ -0,0 +1,1025 @@
+/****************************************************************************
+ * (C) Copyright 2008 Samsung Electronics Co., Ltd., All rights reserved
+ *
+ * [File Name] : Commons3c-otg-transfer-transfer.h
+ * [Description] : This source file implements the functions
+ * to be defined at CommonTransfer Module.
+ * [Author] : Yang Soon Yeal { syatom.yang@samsung.com }
+ * [Department] : System LSI Division/System SW Lab
+ * [Created Date]: 2008/06/03
+ * [Revision History]
+ * (1) 2008/06/03 by Yang Soon Yeal { syatom.yang@samsung.com }
+ * - Created this file and implements some functions of CommonTransfer.
+ * (2) 2008/07/15 by SeungSoo Yang ( ss1.yang@samsung.com )
+ * - Optimizing for performance
+ * (3) 2008/08/18 by SeungSoo Yang ( ss1.yang@samsung.com )
+ * - Modifying for successful rmmod & disconnecting
+ *
+ ****************************************************************************/
+/****************************************************************************
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+
+#include "shost_transfer.h"
+
+/* the header pointer to indicate the ED_list
+ * to manage the struct ed to be created and initiated.
+ */
+static otg_list_head ed_list_head;
+static u32 ref_periodic_transfer;
+
+/**
+ * void enable_sof(void)
+ *
+ * @brief Generate SOF Interrupt.
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+static void enable_sof(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ gintmsk.b.sofintr = 1;
+ update_reg_32(GINTMSK, gintmsk.d32);
+}
+
+/**
+ * void disable_sof(void)
+ *
+ * @brief Stop to generage SOF interrupt
+ *
+ * @param None
+ *
+ * @return None
+ *
+ * @remark
+ *
+ */
+static void disable_sof(void)
+{
+ gintmsk_t gintmsk = {.d32 = 0};
+ gintmsk.b.sofintr = 1;
+ clear_reg_32(GINTMSK, gintmsk.d32);
+}
+
+
+
+/******************************************************************************/
+/*!
+ * @name int cancel_transfer(struct sec_otghost *otghost,
+ * struct ed *parent_ed,
+ * struct td *cancel_td)
+ *
+ * @brief this function cancels to transfer USB Transfer of cancel_td.
+ * this function firstly check whether this cancel_td
+ * is transferring or not. if the cancel_td is transferring, the this
+ * function requests to cancel the USB Transfer
+ * to S3CScheduler. if the parent_ed is for Periodic Transfer, and
+ * there is not any struct td at parent_ed, then this function requests
+ * to release some usb resources for the struct ed to S3CScheduler.
+ * finally this function deletes the cancel_td.
+ *
+ * @param [IN] pUpdateTD = indicates the pointer ot the struct td
+ * to have STransfer to be updated.
+ *
+ * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD.
+ */
+/******************************************************************************/
+static int cancel_transfer(struct sec_otghost *otghost,
+ struct ed *parent_ed, struct td *cancel_td)
+{
+ otg_list_head *tmp_list_p, *tmp_list2_p;
+ int err = USB_ERR_DEQUEUED;
+ bool cond_found = false;
+
+ if (parent_ed == NULL || cancel_td == NULL) {
+ otg_err(1, "%s is null.\n", parent_ed ?
+ "cancel_td" : "parent_ed");
+ return USB_ERR_NOELEMENT;
+ }
+
+ otg_list_for_each_safe(tmp_list_p, tmp_list2_p,
+ &parent_ed->td_list_entry) {
+
+ if (&cancel_td->td_list_entry == tmp_list_p) {
+ cond_found = true;
+ break;
+ }
+ }
+
+ if (cond_found != true) {
+ otg_dbg(OTG_DBG_TRANSFER, "cond_found != true\n");
+ cancel_td->error_code = USB_ERR_NOELEMENT;
+ otg_usbcore_giveback(cancel_td);
+ return cancel_td->error_code;
+ }
+
+
+ if (cancel_td->is_transferring) {
+ if (!parent_ed->ed_status.is_in_transfer_ready_q) {
+ err = cancel_to_transfer_td(otghost, cancel_td);
+
+ parent_ed->ed_status.in_transferring_td = 0;
+
+ if (err != USB_ERR_SUCCESS) {
+ otg_dbg(OTG_DBG_TRANSFER,
+ "cancel_to_transfer_td\n");
+ cancel_td->error_code = err;
+ otg_usbcore_giveback(cancel_td);
+ goto ErrorStatus;
+ }
+
+ otg_list_pop(&cancel_td->td_list_entry);
+ parent_ed->num_td--;
+ }
+
+ } else {
+
+ otg_list_pop(&cancel_td->td_list_entry);
+ parent_ed->num_td--;
+
+ if (parent_ed->num_td == 0) {
+ remove_ed_from_scheduler(parent_ed);
+ parent_ed->is_need_to_insert_scheduler = true;
+ }
+ }
+
+ if (parent_ed->num_td) {
+ parent_ed->is_need_to_insert_scheduler = true;
+ insert_ed_to_scheduler(otghost, parent_ed);
+
+ } else {
+
+ if (parent_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ parent_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+
+ /* Release channel and usb bus resource for
+ * this struct ed. but, not release memory for this ed.
+ */
+ free_usb_resource_for_periodic(
+ parent_ed->ed_desc.used_bus_time,
+ cancel_td->cur_stransfer.alloc_chnum,
+ cancel_td->parent_ed_p->ed_desc.endpoint_type);
+
+ parent_ed->ed_status.is_alloc_resource_for_ed = false;
+ }
+ }
+ /* the caller of this functions should call
+ otg_usbcore_giveback(cancel_td); */
+ cancel_td->error_code = USB_ERR_DEQUEUED;
+ otg_usbcore_giveback(cancel_td);
+
+ /* TODO: recursive call occured. FIX */
+ delete_td(otghost, cancel_td);
+
+ErrorStatus:
+
+ return err;
+}
+
+
+
+
+/******************************************************************************/
+/*!
+ * @name int cancel_all_td(struct sec_otghost *otghost, struct ed *parent_ed)
+ *
+ * @brief this function cancels all Transfer which parent_ed manages.
+ *
+ * @param [IN] parent_ed = indicates the pointer ot the struct ed
+ * to manage TD_ts to be canceled.
+ *
+ * @return
+ * USB_ERR_SUCCESS - if success to cancel all TD_ts of pParentsED.
+ * USB_ERR_FAIL - if fail to cancel all TD_ts of pParentsED.
+ */
+/******************************************************************************/
+static int cancel_all_td(struct sec_otghost *otghost, struct ed *parent_ed)
+{
+ otg_list_head *cancel_td_list_entry;
+ struct td *cancel_td;
+
+ otg_dbg(OTG_DBG_OTGHCDI_HCD, "cancel_all_td\n");
+ do {
+ cancel_td_list_entry = parent_ed->td_list_entry.next;
+
+ cancel_td = otg_list_get_node(cancel_td_list_entry,
+ struct td, td_list_entry);
+
+ cancel_transfer(otghost, parent_ed, cancel_td);
+
+ } while (parent_ed->num_td);
+
+ return USB_ERR_SUCCESS;
+}
+
+
+
+
+/******************************************************************************/
+/*!
+ * @name int delete_ed(struct ed *delete_ed)
+ *
+ * @brief this function delete the delete_ed.
+ * if there is some available TD_ts on delete_ed,
+ * then this function also deletes these struct td
+ *
+ * @param [IN] delete_ed = indicates the address of struct ed to be deleted.
+ *
+ * @return USB_ERR_SUCCESS -if successes to delete the struct ed.
+ * USB_ERR_FAILl -if fails to delete the ed.
+ */
+/******************************************************************************/
+static int delete_ed(struct sec_otghost *otghost, struct ed *delete_ed)
+{
+ otg_kal_make_ep_null(delete_ed);
+
+ if (delete_ed->num_td) {
+ cancel_all_td(otghost, delete_ed);
+ /**
+ * need to giveback of td's urb with considering life-cycle of
+ * TD, ED, urb->hcpriv, td->private, ep->hcpriv, td->parentED
+ * (commented by ss1.yang)
+ */
+ }
+
+ otg_list_pop(&delete_ed->ed_list_entry);
+
+ if (delete_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ delete_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+ ref_periodic_transfer--;
+ }
+
+ if (ref_periodic_transfer == 0)
+ disable_sof();
+
+ otg_mem_free(delete_ed);
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name void init_transfer(void)
+ *
+ * @brief this function initiates the S3CTranfer module.
+ * that is, this functions initiates
+ * the ED_list_head OTG List which manages the all ed to be existed.
+ *
+ * @param void
+ * @return void
+ */
+/******************************************************************************/
+
+static void init_transfer(void)
+{
+ otg_dbg(OTG_DBG_TRANSFER, "start to init_transfer\n");
+ otg_list_init(&ed_list_head);
+ ref_periodic_transfer = 0;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name void DeInitTransfer(void)
+ *
+ * @brief this function Deinitiates the S3CTranfer module.
+ * this functions check which there are
+ * some ed on ED_list_head. if some ed exists,
+ * deinit_transfer() deletes the ed.
+ *
+ * @param void
+ * @return void
+ */
+/******************************************************************************/
+static void deinit_transfer(struct sec_otghost *otghost)
+{
+ otg_list_head *ed_list_member;
+ struct ed *delete_ed_p;
+
+ while (otg_list_empty(&ed_list_head) != true) {
+
+ ed_list_member = ed_list_head.next;
+
+ /* otg_list_pop(ed_list_member); */
+
+ delete_ed_p = otg_list_get_node(ed_list_member,
+ struct ed, ed_list_entry);
+
+ delete_ed(otghost, delete_ed_p);
+ }
+}
+
+/******************************************************************************/
+/*!
+ * @name int delete_td(struct sec_otghost *otghost, struct td *delete_td)
+ *
+ * @brief this function frees memory resource for the delete_td.
+ * and if delete_td is transferring USB Transfer, then
+ * this function request to cancel the USB Transfer to scheduler.
+ *
+ *
+ * @param [OUT] new_td_p = returns the address of the new struct td.
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new struct td.
+ * USB_ERR_FAILl -if fails to create to new struct td.
+ */
+/******************************************************************************/
+int delete_td(struct sec_otghost *otghost, struct td *delete_td)
+{
+ if (delete_td->is_transferring) {
+ /* at this case, we should cancel the USB Transfer. */
+ cancel_to_transfer_td(otghost, delete_td);
+ }
+
+ otg_mem_free(delete_td);
+ return USB_ERR_SUCCESS;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int create_ed(struct ed **new_ed)
+ *
+ * @brief this function creates a new ed and returns the ed to Caller
+ *
+ * @param [OUT] new_ed = returns the address of the new ed .
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new ed.
+ * USB_ERR_FAILl -if fails to create to new ed.
+ */
+/******************************************************************************/
+static int create_ed(struct ed **new_ed)
+{
+ int err_code = USB_ERR_SUCCESS;
+
+ err_code = otg_mem_alloc((void **)new_ed,
+ (u16)sizeof(struct ed), USB_MEM_ASYNC);
+ otg_mem_set(*new_ed, 0, sizeof(struct ed));
+ return err_code;
+}
+
+
+/******************************************************************************/
+/*!
+ * @name int init_ed(struct ed *init_ed,
+ * u8 dev_addr,
+ * u8 ep_num,
+ * bool f_is_ep_in,
+ * u8 dev_speed,
+ * u8 ep_type,
+ * u32 max_packet_size,
+ * u8 multi_count,
+ * u8 interval,
+ * u32 sched_frame,
+ * u8 hub_addr,
+ * u8 hub_port,
+ * bool f_is_do_split)
+ *
+ * @brief this function initiates the init_ed by using the another parameters.
+ *
+ * @param [OUT] init_ed = returns the struct ed to be initiated.
+ * [IN] dev_addr = inidcates the address of USB Device.
+ * [IN] ep_num = inidcates the number of the specific endpoint on USB Device.
+ * [IN] f_is_ep_in = inidcates whether the endpoint is IN or not
+ * [IN] dev_speed = inidcates the speed of USB Device.
+ * [IN] max_packet_size = inidcates the maximum packet size of a specific
+ * endpoint on USB Device.
+ * [IN] multi_count = if the endpoint supports periodic transfer
+ * , this indicates the multiple packet to be transferred on a uframe
+ * [IN] interval= if the endpoint support periodic transfer, this indicates
+ * the polling rate.
+ * [IN] sched_frame= if the endpoint supports periodic transfer, this indicates
+ * the start frame number.
+ * [IN] hub_addr= indicate the address of hub which the USB device attachs to.
+ * [IN] hub_port= inidcates the port number of the hub which the USB
+ * device attachs to.
+ * [IN] f_is_do_split= inidcates whether this tranfer is
+ * split transaction or not.
+ *
+ * @return USB_ERR_SUCCESS -if successes to initiate the ed.
+ * USB_ERR_FAILl -if fails to initiate the ed.
+ * USB_ERR_NOSPACE -if fails to initiate the ed
+ * because there is no USB Resource for this init_ed.
+ */
+/******************************************************************************/
+static int init_ed(struct ed *init_ed,
+ u8 dev_addr,
+ u8 ep_num,
+ bool f_is_ep_in,
+ u8 dev_speed,
+ u8 ep_type,
+ u16 max_packet_size,
+ u8 multi_count,
+ u8 interval,
+ u32 sched_frame,
+ u8 hub_addr,
+ u8 hub_port,
+ bool f_is_do_split,
+ void *ep)
+{
+ init_ed->is_halted = false;
+ init_ed->is_need_to_insert_scheduler = true;
+ init_ed->ed_id = (u32)init_ed;
+ init_ed->num_td = 0;
+ init_ed->ed_private = ep;
+
+ otg_list_init(&init_ed->td_list_entry);
+
+ /* start to initiate struct ed_desc.... */
+ init_ed->ed_desc.is_do_split = f_is_do_split;
+ init_ed->ed_desc.is_ep_in = f_is_ep_in;
+ init_ed->ed_desc.dev_speed = dev_speed;
+ init_ed->ed_desc.hub_addr = hub_addr;
+ init_ed->ed_desc.hub_port = hub_port;
+ init_ed->ed_desc.mc = multi_count;
+ init_ed->ed_desc.device_addr = dev_addr;
+ init_ed->ed_desc.endpoint_num = ep_num;
+ init_ed->ed_desc.endpoint_type = ep_type;
+ init_ed->ed_desc.max_packet_size = max_packet_size;
+ init_ed->ed_desc.sched_frame = sched_frame;
+
+ if (init_ed->ed_desc.endpoint_type == INT_TRANSFER) {
+
+ if (init_ed->ed_desc.dev_speed == LOW_SPEED_OTG ||
+ init_ed->ed_desc.dev_speed == FULL_SPEED_OTG) {
+
+ init_ed->ed_desc.interval = interval;
+
+ } else if (init_ed->ed_desc.dev_speed == HIGH_SPEED_OTG) {
+
+ u8 count = 0;
+ u8 cal_interval = 1;
+
+ for (count = 0;
+ count < (init_ed->ed_desc.interval-1); count++)
+ cal_interval *= 2;
+
+ init_ed->ed_desc.interval = cal_interval;
+
+ } else {
+ otg_dbg(OTG_DBG_TRANSFER,
+ "Super-Speed is not supported\n");
+ }
+
+ init_ed->ed_desc.sched_frame =
+ (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM;
+ ref_periodic_transfer++;
+ }
+
+ if (init_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+ u8 count = 0;
+ u8 cal_interval = 1;
+
+ for (count = 0; count < (init_ed->ed_desc.interval-1); count++)
+ cal_interval *= 2;
+
+ init_ed->ed_desc.interval = cal_interval;
+ init_ed->ed_desc.sched_frame =
+ (SCHEDULE_SLOT+oci_get_frame_num())&HFNUM_MAX_FRNUM;
+ ref_periodic_transfer++;
+ }
+
+ /* start to initiate struct ed_status.... */
+
+ /* initiates PID */
+ switch (ep_type) {
+ case BULK_TRANSFER:
+ case INT_TRANSFER:
+ init_ed->ed_status.data_toggle = DATA0;
+ break;
+
+ case CONTROL_TRANSFER:
+ init_ed->ed_status.control_data_toggle.setup = SETUP;
+ init_ed->ed_status.control_data_toggle.data = DATA1;
+ init_ed->ed_status.control_data_toggle.status = DATA1;
+ break;
+
+ case ISOCH_TRANSFER:
+ if (f_is_ep_in) {
+ switch (multi_count) {
+ case MULTI_COUNT_ZERO:
+ init_ed->ed_status.data_toggle = DATA0;
+ break;
+ case MULTI_COUNT_ONE:
+ init_ed->ed_status.data_toggle = DATA1;
+ break;
+ case MULTI_COUNT_TWO:
+ init_ed->ed_status.data_toggle = DATA2;
+ break;
+ default:
+ break;
+ }
+
+ } else {
+ switch (multi_count) {
+ case MULTI_COUNT_ZERO:
+ init_ed->ed_status.data_toggle = DATA0;
+ break;
+ case MULTI_COUNT_ONE:
+ init_ed->ed_status.data_toggle = MDATA;
+ break;
+ case MULTI_COUNT_TWO:
+ init_ed->ed_status.data_toggle = MDATA;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (init_ed->ed_desc.endpoint_type == INT_TRANSFER ||
+ init_ed->ed_desc.endpoint_type == ISOCH_TRANSFER) {
+
+ u32 usb_time = 0, byte_count = 0;
+
+ /* calculates the bytes to be transferred
+ at one (uframe)frame.*/
+ byte_count = (init_ed->ed_desc.mc+1) *
+ init_ed->ed_desc.max_packet_size;
+
+ usb_time = (u32)otg_usbcore_get_calc_bustime(
+ init_ed->ed_desc.dev_speed,
+ init_ed->ed_desc.is_ep_in,
+ (init_ed->ed_desc.endpoint_type ==
+ ISOCH_TRANSFER ? true : false), byte_count);
+
+ usb_time /= 1000; /* convert nanosec unit to usec unit */
+
+ if (reserve_used_resource_for_periodic(usb_time,
+ init_ed->ed_desc.dev_speed,
+ init_ed->ed_desc.endpoint_type)
+ != USB_ERR_SUCCESS) {
+ return USB_ERR_NOSPACE;
+ }
+
+ init_ed->ed_status.is_alloc_resource_for_ed = true;
+ init_ed->ed_desc.used_bus_time = usb_time;
+ init_ed->ed_desc.mc = multi_count+1;
+ }
+
+ init_ed->ed_status.is_in_transfer_ready_q = false;
+ init_ed->ed_status.is_in_transferring = false;
+ init_ed->ed_status.is_ping_enable = false;
+ init_ed->ed_status.in_transferring_td = 0;
+
+ /* push the ed to ED_list. */
+ otg_list_push_prev(&init_ed->ed_list_entry, &ed_list_head);
+
+ if (ref_periodic_transfer)
+ enable_sof();
+
+ return USB_ERR_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/*!
+ * @name int create_td(struct td **new_td)
+ *
+ * @brief this function creates a new struct td and returns the struct td to Caller
+ *
+ *
+ * @param [OUT] new_td = returns the address of the new struct td .
+ *
+ * @return USB_ERR_SUCCESS -if successes to create the new struct td.
+ * USB_ERR_FAILl -if fails to create to new struct td.
+ */
+/*****************************************************************************/
+static int create_td(struct td **new_td)
+{
+ int err_code = USB_ERR_SUCCESS;
+
+ err_code = otg_mem_alloc((void **)new_td,
+ (u16)sizeof(struct td), USB_MEM_ASYNC);
+ otg_mem_set(*new_td, 0, sizeof(struct td));
+ return err_code;
+}
+
+
+/*****************************************************************************/
+/*!
+ * @name int init_perio_stransfer( bool f_is_isoch_transfer,
+ * struct td *parent_td)
+ *
+ * @brief this function initiates the parent_td->cur_stransfer for Periodic
+ * Transfer and inserts this init_td_p to init_td_p->parent_ed_p.
+ *
+ * @param [IN] f_is_isoch_transfer = indicates whether this transfer
+ * is Isochronous or not.
+ * [IN] parent_td = indicates the address of struct td to be initiated.
+ *
+ * @return USB_ERR_SUCCESS -if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL -if fail to update the STranfer of pUpdateTD.
+ */
+/*****************************************************************************/
+static int init_perio_stransfer(bool f_is_isoch_transfer,
+ struct td *parent_td)
+{
+ parent_td->cur_stransfer.ed_desc_p =
+ &parent_td->parent_ed_p->ed_desc;
+ parent_td->cur_stransfer.ed_status_p =
+ &parent_td->parent_ed_p->ed_status;
+ parent_td->cur_stransfer.alloc_chnum = CH_NONE;
+ parent_td->cur_stransfer.parent_td =
+ (u32)parent_td;
+ parent_td->cur_stransfer.stransfer_id =
+ (u32)&parent_td->cur_stransfer;
+
+ otg_mem_set(&parent_td->cur_stransfer.hc_reg, 0, sizeof(struct hc_reg));
+
+ parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1;
+
+ if (f_is_isoch_transfer) {
+ /* initiates the STransfer usinb the IsochPacketDesc[0]. */
+ parent_td->cur_stransfer.buf_size =
+ parent_td->isoch_packet_desc_p[0].buf_size;
+
+ parent_td->cur_stransfer.phy_addr =
+ parent_td->phy_buf_addr +
+ parent_td->isoch_packet_desc_p[0].
+ isoch_packiet_start_addr;
+
+ parent_td->cur_stransfer.vir_addr =
+ parent_td->vir_buf_addr +
+ parent_td->isoch_packet_desc_p[0].
+ isoch_packiet_start_addr;
+
+ } else {
+ parent_td->cur_stransfer.buf_size =
+ (parent_td->buf_size > MAX_CH_TRANSFER_SIZE) ?
+ MAX_CH_TRANSFER_SIZE : parent_td->buf_size;
+
+ parent_td->cur_stransfer.phy_addr = parent_td->phy_buf_addr;
+ parent_td->cur_stransfer.vir_addr = parent_td->vir_buf_addr;
+ }
+
+ parent_td->cur_stransfer.packet_cnt =
+ calc_packet_cnt(parent_td->cur_stransfer.buf_size,
+ parent_td->parent_ed_p->ed_desc.max_packet_size);
+
+ return USB_ERR_SUCCESS;
+}
+
+
+/*****************************************************************************/
+/*!
+ * @name int init_nonperio_stransfer(bool f_is_standard_dev_req,
+ * struct td *parent_td)
+ *
+ * @brief this function initiates the parent_td->cur_stransfer
+ * for NonPeriodic Transfer and inserts this init_td_p
+ * to init_td_p->parent_ed_p.
+ *
+ * @param [IN] f_is_standard_dev_req- indicates whether this is Control or not.
+ * [IN] parent_td- indicates the address of struct td to be initiated.
+ *
+ * @return USB_ERR_SUCCESS - if success to update the STranfer of pUpdateTD.
+ * USB_ERR_FAIL - if fail to update the STranfer of pUpdateTD.
+ */
+/******************************************************************************/
+static int init_nonperio_stransfer(bool f_is_standard_dev_req,
+ struct td *parent_td)
+{
+ parent_td->cur_stransfer.ed_desc_p =
+ &parent_td->parent_ed_p->ed_desc;
+ parent_td->cur_stransfer.ed_status_p =
+ &parent_td->parent_ed_p->ed_status;
+ parent_td->cur_stransfer.alloc_chnum = CH_NONE;
+ parent_td->cur_stransfer.parent_td = (u32)parent_td;
+ parent_td->cur_stransfer.stransfer_id =
+ (u32)&parent_td->cur_stransfer;
+
+ otg_mem_set(&(parent_td->cur_stransfer.hc_reg),
+ 0, sizeof(struct hc_reg));
+
+ parent_td->cur_stransfer.hc_reg.hc_int_msk.b.chhltd = 1;
+
+ if (f_is_standard_dev_req) {
+ parent_td->cur_stransfer.buf_size =
+ USB_20_STAND_DEV_REQUEST_SIZE;
+ parent_td->cur_stransfer.phy_addr =
+ parent_td->standard_dev_req_info.
+ phy_standard_dev_req_addr;
+ parent_td->cur_stransfer.vir_addr =
+ parent_td->standard_dev_req_info.
+ vir_standard_dev_req_addr;
+ } else {
+ parent_td->cur_stransfer.buf_size
+ = (parent_td->buf_size > MAX_CH_TRANSFER_SIZE)
+ ? MAX_CH_TRANSFER_SIZE
+ : parent_td->buf_size;
+
+ parent_td->cur_stransfer.phy_addr
+ = parent_td->phy_buf_addr;
+ parent_td->cur_stransfer.vir_addr
+ = parent_td->vir_buf_addr;
+ }
+
+ parent_td->cur_stransfer.packet_cnt =
+ calc_packet_cnt(parent_td->cur_stransfer.buf_size,
+ parent_td->parent_ed_p->ed_desc.max_packet_size);
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int init_td(struct td *init_td,
+ * struct ed *parent_ed,
+ * void *call_back_fun,
+ * void *call_back_param,
+ * u32 transfer_flag,
+ * bool f_is_standard_dev_req,
+ * u32 phy_setup,
+ * u32 vir_setup,
+ * u32 vir_buf_addr,
+ * u32 phy_buf_addr,
+ * u32 buf_size,
+ * u32 isoch_start_frame,
+ * isoch_packet_desc_t *isoch_packet_desc,
+ * u32 isoch_packet_num,
+ * void *td_priv)
+ *
+ * @brief this function initiates the init_td by using another parameter.
+ *
+ *
+ * @param [IN] init_td- indicate the struct td to be initiated.
+ * [IN] parent_ed- indicate the ed to manage this init_td
+ * [IN] call_back_func- indicate the call-back function of application.
+ * [IN] call_back_param- indicate the parameter of the call-back function.
+ * [IN] transfer_flag- indicate the transfer flag.
+ * [IN] f_is_standard_dev_req- indicates the issue transfer request is Request
+ * [IN] phy_setup- the physical address of buffer to store the Request.
+ * [IN] vir_setup- the virtual address of buffer to store the Request.
+ * [IN] vir_buf_addr- the virtual address of buffer to store the data
+ * to be transferred or received.
+ * [IN] phy_buf_addr- the physical address of buffer to store the data
+ * to be transferred or received.
+ * [IN] buf_size- indicates the buffer size.
+ * [IN] isoch_start_frame- if this usb transfer is isochronous transfer
+ * , this indicates the start frame to start the usb transfer.
+ * [IN] isoch_packet_desc- if the usb transfer is isochronous transfer
+ * , this indicates the structure to describe the isochronous transfer.
+ * [IN] isoch_packet_num- if the usb transfer is isochronous transfer
+ * , this indicates the number of packet to consist of the usb transfer.
+ * [IN] td_priv- indicate the private data to be delivered from usb core.
+ * td_priv stores the urb of linux.
+ *
+ * @return USB_ERR_SUCCESS -if successes to initiate the new struct td.
+ * USB_ERR_FAILl -if fails to create to new struct td.
+ */
+/******************************************************************************/
+static int init_td(struct td *init_td,
+ struct ed *parent_ed,
+ void *call_back_fun,
+ void *call_back_param,
+ u32 transfer_flag,
+ bool f_is_standard_dev_req,
+ u32 phy_setup,
+ u32 vir_setup,
+ u32 vir_buf_addr,
+ u32 phy_buf_addr,
+ u32 buf_size,
+ u32 isoch_start_frame,
+ struct isoch_packet_desc *isoch_packet_desc,
+ u32 isoch_packet_num,
+ void *td_priv)
+{
+ if (f_is_standard_dev_req) {
+
+ if ((phy_buf_addr > 0) && (buf_size > 0))
+ init_td->standard_dev_req_info.is_data_stage = true;
+ else
+ init_td->standard_dev_req_info.is_data_stage = false;
+
+ init_td->standard_dev_req_info.conrol_transfer_stage
+ = SETUP_STAGE;
+ init_td->standard_dev_req_info.phy_standard_dev_req_addr
+ = phy_setup;
+ init_td->standard_dev_req_info.vir_standard_dev_req_addr
+ = vir_setup;
+ }
+
+ init_td->call_back_func_p = call_back_fun;
+ init_td->call_back_func_param_p = call_back_param;
+ init_td->error_code = USB_ERR_SUCCESS;
+ init_td->is_standard_dev_req = f_is_standard_dev_req;
+ init_td->is_transfer_done = false;
+ init_td->is_transferring = false;
+ init_td->td_private = td_priv;
+ init_td->err_cnt = 0;
+ init_td->parent_ed_p = parent_ed;
+ init_td->phy_buf_addr = phy_buf_addr;
+ init_td->vir_buf_addr = vir_buf_addr;
+ init_td->buf_size = buf_size;
+ init_td->isoch_packet_desc_p = isoch_packet_desc;
+ init_td->isoch_packet_num = isoch_packet_num;
+ init_td->isoch_packet_index = 0;
+ init_td->isoch_packet_position = 0;
+ init_td->sched_frame = isoch_start_frame;
+ init_td->used_total_bus_time = parent_ed->ed_desc.used_bus_time;
+ init_td->td_id = (u32)init_td;
+ init_td->transfer_flag = transfer_flag;
+ init_td->transferred_szie = 0;
+
+ switch (parent_ed->ed_desc.endpoint_type) {
+ case CONTROL_TRANSFER:
+ init_nonperio_stransfer(true, init_td);
+ break;
+
+ case BULK_TRANSFER:
+ init_nonperio_stransfer(false, init_td);
+ break;
+
+ case INT_TRANSFER:
+ init_perio_stransfer(false, init_td);
+ break;
+
+ case ISOCH_TRANSFER:
+ init_perio_stransfer(true, init_td);
+ break;
+
+ default:
+ return USB_ERR_FAIL;
+ }
+
+ /* insert the struct td to parent_ed->td_list_entry. */
+ otg_list_push_prev(&init_td->td_list_entry, &parent_ed->td_list_entry);
+ parent_ed->num_td++;
+
+ return USB_ERR_SUCCESS;
+}
+
+/******************************************************************************/
+/*!
+ * @name int issue_transfer(struct sec_otghost *otghost,
+ * struct ed *parent_ed,
+ * void *call_back_func,
+ * void *call_back_param,
+ * u32 transfer_flag,
+ * bool f_is_standard_dev_req,
+ * u32 setup_vir_addr,
+ * u32 setup_phy_addr,
+ * u32 vir_buf_addr,
+ * u32 phy_buf_addr,
+ * u32 buf_size,
+ * u32 start_frame,
+ * u32 isoch_packet_num,
+ * isoch_packet_desc_t *isoch_packet_desc,
+ * void *td_priv,
+ * unsigned int *return_td_addr)
+ *
+ * @brief this function start USB Transfer
+ *
+ *
+ * @param [IN] parent_ed - indicate the ed to manage this issue transfer.
+ * [IN] call_back_func - indicate the call-back function of application.
+ * [IN] call_back_param - indicate the parameter of the call-back function.
+ * [IN] transfer_flag - indicate the transfer flag.
+ * [IN] f_is_standard_dev_req - indicates the issue transfer request
+ * is USB Standard Request
+ * [IN] setup_vir_addr - the virtual address of buffer to store the Request.
+ * [IN] setup_phy_addr - the physical address of buffer to store the Request.
+ * [IN] vir_buf_addr - the virtual address of buffer to store the data
+ * to be transferred or received.
+ * [IN] phy_buf_addr - the physical address of buffer to store the data
+ * to be transferred or received.
+ * [IN] buf_siz- indicates the buffer size.
+ * [IN] start_frame - if this usb transfer is isochronous transfer,
+ * this indicates the start frame to start the usb transfer.
+ * [IN] isoch_packet_num - if the usb transfer is isochronous transfer,
+ * this indicates the number of packet to consist of the usb transfer.
+ * [IN] isoch_packet_desc - if the usb transfer is isochronous transfer
+ * this indicates the structure to describe the isochronous transfer.
+ * [IN] td_priv - indicate the private data to be delivered from usb core.
+ * td_priv stores the urb of linux.
+ * [OUT] return_td_addr - indicates the variable address
+ * to store the new struct td for this transfer
+ *
+ * @return USB_ERR_SUCCESS - if successes to initiate the new struct td.
+ * USB_ERR_FAILl - if fails to create to new struct td.
+ */
+/******************************************************************************/
+static int issue_transfer(struct sec_otghost *otghost,
+ struct ed *parent_ed,
+ void *call_back_func,
+ void *call_back_param,
+ u32 transfer_flag,
+ bool f_is_standard_dev_req,
+ u32 setup_vir_addr,
+ u32 setup_phy_addr,
+ u32 vir_buf_addr,
+ u32 phy_buf_addr,
+ u32 buf_size,
+ u32 start_frame,
+ u32 isoch_packet_num,
+ struct isoch_packet_desc *isoch_packet_desc,
+ void *td_priv,
+ unsigned int *return_td_addr)
+{
+ struct td *new_td_p = NULL;
+ int err = USB_ERR_SUCCESS;
+
+ if (create_td(&new_td_p) == USB_ERR_SUCCESS) {
+ err = init_td(new_td_p,
+ parent_ed,
+ call_back_func,
+ call_back_param,
+ transfer_flag,
+ f_is_standard_dev_req,
+ setup_phy_addr,
+ setup_vir_addr,
+ vir_buf_addr,
+ phy_buf_addr,
+ buf_size,
+ start_frame,
+ isoch_packet_desc,
+ isoch_packet_num,
+ td_priv);
+
+ if (err != USB_ERR_SUCCESS)
+ return USB_ERR_NOMEM;
+
+ if (parent_ed->is_need_to_insert_scheduler)
+ insert_ed_to_scheduler(otghost, parent_ed);
+
+ *return_td_addr = (u32)new_td_p;
+
+ return USB_ERR_SUCCESS;
+ } else
+ return USB_ERR_NOMEM;
+}
+
+
+/* TODO: not used. removed */
+#if 0
+static int create_isoch_packet_desc(isoch_packet_desc_t **new_isoch_packet_desc,
+ u32 isoch_packet_num)
+{
+ return otg_mem_alloc((void **)new_isoch_packet_desc,
+ (u16)sizeof(isoch_packet_desc_t)*isoch_packet_num,
+ USB_MEM_SYNC);
+}
+
+static int delete_isoch_packet_desc(isoch_packet_desc_t *del_isoch_packet_desc,
+ u32 isoch_packet_num)
+{
+ return otg_mem_free(del_isoch_packet_desc);
+}
+
+/******************************************************************************/
+/*!
+ * @name
+ * void init_isoch_packet_desc(isoch_packet_desc_t *init_isoch_packet_desc,
+ * u32 isoch_packet_start_addr,
+ * u32 isoch_packet_size,
+ * u32 index)
+ *
+ * @brief this function initiates the isoch_packet_desc_t[index].
+ *
+ * @param
+ * [OUT] init_isoch_packet_desc = indicates the pointer of
+ * IsochPackDesc_t to be initiated.
+ * [IN] isoch_packet_start_addr = indicates the start address of the buffer
+ * to be used at USB Isochronous Transfer.
+ * [IN] isoch_packet_size = indicates the size of Isochronous packet.
+ * [IN] index = indicates the index to be mapped with this.
+ *
+ * @return void
+ */
+/******************************************************************************/
+static void init_isoch_packet_desc(isoch_packet_desc_t *init_isoch_packet_desc,
+ u32 isoch_packet_start_addr,
+ u32 isoch_packet_size,
+ u32 index)
+{
+ init_isoch_packet_desc[index].buf_size = isoch_packet_size;
+ init_isoch_packet_desc[index].isoch_packiet_start_addr
+ = isoch_packet_start_addr;
+ init_isoch_packet_desc[index].isoch_status = 0;
+ init_isoch_packet_desc[index].transferred_szie = 0;
+}
+#endif
+
+