staticint mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size);
/* This function handles received packet. Necessary action is taken based on * cmd/event/data.
*/ staticint mwifiex_usb_recv(struct mwifiex_adapter *adapter, struct sk_buff *skb, u8 ep)
{
u32 recv_type;
__le32 tmp; int ret;
if (adapter->hs_activated)
mwifiex_process_hs_config(adapter);
if (recv_length) { if (urb->status ||
test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) {
mwifiex_dbg(adapter, ERROR, "URB status is failed: %d\n", urb->status); /* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep)
dev_kfree_skb_any(skb); goto setup_for_next;
} if (skb->len > recv_length)
skb_trim(skb, recv_length); else
skb_put(skb, recv_length - skb->len);
status = mwifiex_usb_recv(adapter, skb, context->ep);
/* urb for data_ep is re-submitted now; * urb for cmd_ep will be re-submitted in callback * mwifiex_usb_recv_complete
*/ if (card->rx_cmd_ep == context->ep) return;
} else { if (status == -1)
mwifiex_dbg(adapter, ERROR, "received data processing failed!\n");
/* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep)
dev_kfree_skb_any(skb);
}
} elseif (urb->status) { if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) {
mwifiex_dbg(adapter, FATAL, "Card is removed: %d\n", urb->status);
set_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags);
}
dev_kfree_skb_any(skb); return;
} else { /* Do not free skb in case of command ep */ if (card->rx_cmd_ep != context->ep)
dev_kfree_skb_any(skb);
if (atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) if (card->rx_data_list[i].urb)
usb_kill_urb(card->rx_data_list[i].urb);
for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) {
usb_free_urb(card->rx_data_list[i].urb);
card->rx_data_list[i].urb = NULL;
}
for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
port = &card->port[i]; for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) {
usb_kill_urb(port->tx_data_list[j].urb);
usb_free_urb(port->tx_data_list[j].urb);
port->tx_data_list[j].urb = NULL;
}
}
/* PID_1 is used for firmware downloading only */ switch (id_product) { case USB8766_PID_1: case USB8797_PID_1: case USB8801_PID_1: case USB8997_PID_1:
card->usb_boot_state = USB8XXX_FW_DNLD; break; case USB8766_PID_2: case USB8797_PID_2: case USB8801_PID_2: case USB8997_PID_2:
card->usb_boot_state = USB8XXX_FW_READY; break; default:
pr_warn("unknown id_product %#x\n", id_product);
card->usb_boot_state = USB8XXX_FW_DNLD; break;
}
switch (card->usb_boot_state) { case USB8XXX_FW_DNLD: /* Reject broken descriptors. */ if (!card->rx_cmd_ep || !card->tx_cmd_ep) return -ENODEV; if (card->bulk_out_maxpktsize == 0) return -ENODEV; break; case USB8XXX_FW_READY: /* Assume the driver can handle missing endpoints for now. */ break; default:
WARN_ON(1); return -ENODEV;
}
usb_set_intfdata(intf, card);
ret = mwifiex_add_card(card, &card->fw_done, &usb_ops,
MWIFIEX_USB, &card->udev->dev); if (ret) {
pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret);
usb_reset_device(udev); return ret;
}
usb_get_dev(udev);
return 0;
}
/* Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not suspended, this function allocates and sends a * 'host sleep activate' request to the firmware and turns off the traffic.
*/ staticint mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message)
{ struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; struct usb_tx_data_port *port; int i, j;
/* Might still be loading firmware */
wait_for_completion(&card->fw_done);
adapter = card->adapter; if (!adapter) {
dev_err(&intf->dev, "card is not valid\n"); return 0;
}
if (unlikely(test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)))
mwifiex_dbg(adapter, WARN, "Device already suspended\n");
/* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) {
mwifiex_dbg(adapter, ERROR, "cmd: failed to suspend\n");
clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); return -EFAULT;
}
/* 'MWIFIEX_IS_SUSPENDED' bit indicates device is suspended. * It must be set here before the usb_kill_urb() calls. Reason * is in the complete handlers, urb->status(= -ENOENT) and * this flag is used in combination to distinguish between a * 'suspended' state and a 'disconnect' one.
*/
set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags);
if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb)
usb_kill_urb(card->rx_cmd.urb);
if (atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) if (card->rx_data_list[i].urb)
usb_kill_urb(card->rx_data_list[i].urb);
for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) {
port = &card->port[i]; for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { if (port->tx_data_list[j].urb)
usb_kill_urb(port->tx_data_list[j].urb);
}
}
if (card->tx_cmd.urb)
usb_kill_urb(card->tx_cmd.urb);
return 0;
}
/* Kernel needs to suspend all functions separately. Therefore all * registered functions must have drivers with suspend and resume * methods. Failing that the kernel simply removes the whole card. * * If already not resumed, this function turns on the traffic and * sends a 'host sleep cancel' request to the firmware.
*/ staticint mwifiex_usb_resume(struct usb_interface *intf)
{ struct usb_card_rec *card = usb_get_intfdata(intf); struct mwifiex_adapter *adapter; int i;
if (!card->adapter) {
dev_err(&intf->dev, "%s: card->adapter is NULL\n",
__func__); return 0;
}
adapter = card->adapter;
/* Indicate device resumed. The netdev queue will be resumed only * after the urbs have been re-submitted
*/
clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags);
if (!atomic_read(&card->rx_data_urb_pending)) for (i = 0; i < MWIFIEX_RX_DATA_URB; i++)
mwifiex_usb_submit_rx_urb(&card->rx_data_list[i],
MWIFIEX_RX_DATA_BUF_SIZE);
if (!atomic_read(&card->rx_cmd_urb_pending)) {
card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); if (card->rx_cmd.skb)
mwifiex_usb_submit_rx_urb(&card->rx_cmd,
MWIFIEX_RX_CMD_BUF_SIZE);
}
/* Disable Host Sleep */ if (adapter->hs_activated)
mwifiex_cancel_hs(mwifiex_get_priv(adapter,
MWIFIEX_BSS_ROLE_ANY),
MWIFIEX_ASYNC_CMD);
/* Packets in aggr_list will be send in either skb_aggr or * write complete, delete the tx_aggr timer
*/ if (port->tx_aggr.timer_cnxt.is_hold_timer_set) {
timer_delete(&port->tx_aggr.timer_cnxt.hold_timer);
port->tx_aggr.timer_cnxt.is_hold_timer_set = false;
port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0;
}
/* This function prepare data packet to be send under usb tx aggregation * protocol, check current usb aggregation status, link packet to aggrgation * list if possible, work flow as below: * (1) if only 1 packet available, add usb tx aggregation header and send. * (2) if packet is able to aggregated, link it to current aggregation list. * (3) if packet is not able to aggregated, aggregate and send exist packets * in aggrgation list. Then, link packet in the list if there is more * packet in transmit queue, otherwise try to transmit single packet.
*/ staticint mwifiex_usb_aggr_tx_data(struct mwifiex_adapter *adapter, u8 ep, struct sk_buff *skb, struct mwifiex_tx_param *tx_param, struct usb_tx_data_port *port)
{
u8 *payload, pad;
u16 align = adapter->bus_aggr.tx_aggr_align; struct sk_buff *skb_send = NULL; struct urb_context *context = NULL; struct txpd *local_tx_pd =
(struct txpd *)((u8 *)skb->data + adapter->intf_hdr_len);
u8 f_send_aggr_buf = 0;
u8 f_send_cur_buf = 0;
u8 f_precopy_cur_buf = 0;
u8 f_postcopy_cur_buf = 0;
u32 timeout; int ret;
/* padding to ensure each packet alginment */
pad = (align - (skb->len & (align - 1))) % align;
if (tx_param && tx_param->next_pkt_len) { /* next packet available in tx queue*/ if (port->tx_aggr.aggr_len + skb->len + pad >
adapter->bus_aggr.tx_aggr_max_size) {
f_send_aggr_buf = 1;
f_postcopy_cur_buf = 1;
} else { /* current packet could be aggregated*/
f_precopy_cur_buf = 1;
if (port->tx_aggr.aggr_len + skb->len + pad +
tx_param->next_pkt_len >
adapter->bus_aggr.tx_aggr_max_size ||
port->tx_aggr.aggr_num + 2 >
adapter->bus_aggr.tx_aggr_max_num) { /* next packet could not be aggregated * send current aggregation buffer
*/
f_send_aggr_buf = 1;
}
}
} else { /* last packet in tx queue */ if (port->tx_aggr.aggr_num > 0) { /* pending packets in aggregation buffer*/ if (port->tx_aggr.aggr_len + skb->len + pad >
adapter->bus_aggr.tx_aggr_max_size) { /* current packet not be able to aggregated, * send aggr buffer first, then send packet.
*/
f_send_cur_buf = 1;
} else { /* last packet, Aggregation and send */
f_precopy_cur_buf = 1;
}
f_send_aggr_buf = 1;
} else { /* no pending packets in aggregation buffer, * send current packet immediately
*/
f_send_cur_buf = 1;
}
}
if (f_precopy_cur_buf) {
skb_queue_tail(&port->tx_aggr.aggr_list, skb);
port->tx_aggr.aggr_len += (skb->len + pad);
port->tx_aggr.aggr_num++; if (f_send_aggr_buf) goto send_aggr_buf;
/* packet will not been send immediately, * set a timer to make sure it will be sent under * strict time limit. Dynamically fit the timeout * value, according to packets number in aggr_list
*/ if (!port->tx_aggr.timer_cnxt.is_hold_timer_set) {
port->tx_aggr.timer_cnxt.hold_tmo_msecs =
MWIFIEX_USB_TX_AGGR_TMO_MIN;
timeout =
port->tx_aggr.timer_cnxt.hold_tmo_msecs;
mod_timer(&port->tx_aggr.timer_cnxt.hold_timer,
jiffies + msecs_to_jiffies(timeout));
port->tx_aggr.timer_cnxt.is_hold_timer_set = true;
} else { if (port->tx_aggr.timer_cnxt.hold_tmo_msecs <
MWIFIEX_USB_TX_AGGR_TMO_MAX) { /* Dyanmic fit timeout */
timeout =
++port->tx_aggr.timer_cnxt.hold_tmo_msecs;
mod_timer(&port->tx_aggr.timer_cnxt.hold_timer,
jiffies + msecs_to_jiffies(timeout));
}
}
}
send_aggr_buf: if (f_send_aggr_buf) {
ret = mwifiex_usb_prepare_tx_aggr_skb(adapter, port, &skb_send); if (!ret) {
context = &port->tx_data_list[port->tx_data_ix++];
ret = mwifiex_usb_construct_send_urb(adapter, port, ep,
context, skb_send); if (ret == -1)
mwifiex_write_data_complete(adapter, skb_send,
0, -1);
}
}
if (f_send_cur_buf) { if (f_send_aggr_buf) { if (atomic_read(&port->tx_data_urb_pending) >=
MWIFIEX_TX_DATA_URB) {
port->block_status = true;
adapter->data_sent =
mwifiex_usb_data_sent(adapter); /* no available urb, postcopy packet*/
f_postcopy_cur_buf = 1; goto postcopy_cur_buf;
}
if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB)
port->tx_data_ix = 0;
}
if (!firmware) {
mwifiex_dbg(adapter, ERROR, "No firmware image found! Terminating download\n");
ret = -1; goto fw_exit;
}
/* Allocate memory for transmit */
fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); if (!fwdata) {
ret = -ENOMEM; goto fw_exit;
}
/* Allocate memory for receive */
recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); if (!recv_buff) {
ret = -ENOMEM; goto cleanup;
}
do { /* Send pseudo data to check winner status first */ if (check_winner) {
memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header));
dlen = 0;
} else { /* copy the header of the fw_data to get the length */
memcpy(&fwdata->fw_hdr, &firmware[tlen], sizeof(struct fw_header));
/* check 1st firmware block resp for highest bit set */ if (check_winner) { if (le32_to_cpu(sync_fw.cmd) & 0x80000000) {
mwifiex_dbg(adapter, WARN, "USB is not the winner %#x\n",
sync_fw.cmd);
/* returning success */
ret = 0; goto cleanup;
}
mwifiex_dbg(adapter, MSG, "start to download FW...\n");
check_winner = 0; break;
}
/* check the firmware block response for CRC errors */ if (sync_fw.cmd) {
mwifiex_dbg(adapter, ERROR, "FW received block with CRC %#x\n",
sync_fw.cmd);
ret = -1; continue;
}
for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { if (card->rx_data_list[i].skb) continue;
ctx = &card->rx_data_list[i];
mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE);
}
}
/* This function is called after the card has woken up. */ staticinlineint
mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter)
{ return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.