/************************************************************************ *Copyright(C) 2020-2021: Seekwave tech LTD China *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "skw_usb.h" #include "skw_usb_log.h" #include "skw_usb_debugfs.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) #include #else #include #endif #ifndef GENMASK #ifdef CONFIG_64BIT #define BITS_PER_LONG 64 #else #define BITS_PER_LONG 32 #endif /* CONFIG_64BIT */ #define GENMASK(h, l) \ (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) #endif #define MAX_BUFFER_SIZE 20*1024 #define MAX_MSG_SIZE MAX_BUFFER_SIZE #define VENDOR_MSG_MODEM_ASSERT 0xA5 #define VENDOR_MSG_SERVICE_CTRL 0xA6 #define VENDOR_MSG_PACKET_COUNT 0xA7 #define VENDOR_MSG_LOG_SWITCH 0xA8 #define VENDOR_MSG_MODEM_RESET 0xA9 #define VENDOR_MSG_MODEM_SUSP 0xAA #define WIFI_SERVICE 0 #define BT_SERVICE 1 #define SERVICE_START 0 #define SERVICE_STOP 1 #define MODEM_OFF 0 #define MODEM_ON 1 #define MODEM_HALT 2 #define MODEM_DOWNLOAD_FAILED 4 #define WIFI_PORT_SHARE_FLAG 0x4000 //#define MODEM_WDT_SUPPORT 0x40 #define USB_HOST_RESUME_SUPPORT 0x20 #define MAX_USB_PORT MAX_PORT_COUNT #define MAX_PACKET_COUNT 20 static struct delayed_work skw_except_work; static struct work_struct add_device_work; static struct work_struct dump_memory_worker; static struct work_struct usb_control_worker; static struct platform_device *wifi_data_pdev; static u64 port_dmamask = DMA_BIT_MASK(32); static u32 service_state_map = 0; static int cp_log_status = 0; static char *firmware_data; static int firmware_size; static int firmware_addr; static struct seekwave_device *usb_boot_data; static struct completion download_done; static struct completion loop_completion; static BLOCKING_NOTIFIER_HEAD(modem_notifier_list); static int chip_en_gpio; static int host_wake_gpio; static int modem_status; static int usb_speed_switching; static int cls_recovery_mode_en; static char *skw_chipid; static u32 last_sent_wifi_cmd[3]; static u32 last_recv_wifi_evt[3]; static u32 last_recv_wifi_ack[3]; static u64 last_sent_time, last_ack_time; static struct scatterlist *sgs; static int nr_sgs; static int start_service_flag = 0; static u16 transfer_index; /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static const struct usb_device_id skw_usb_io_id_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x3607, 0x02, 0x02, 0) }, { USB_DEVICE(0x0483, 0x5720) }, { USB_DEVICE(0x0483, 0x5721) }, { USB_DEVICE(0x3607, 0x6316) }, { USB_DEVICE(0x3607, 0x6621) }, { USB_DEVICE(0x3607, 0x6160) }, {} /* Terminating entry */ }; /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy:XW * ********************************************************************* */ static struct recovery_data{ struct mutex except_mutex; int cp_state; } g_recovery_data; #ifdef CONFIG_SKW_DL_TIME_STATS ktime_t cur_time,last_time; #endif #define SKW_USB_GET_RECOVERY_DATA() &g_recovery_data static struct usb_port_struct { struct work_struct work; struct platform_device *pdev; int portno; struct usb_interface *interface; struct usb_device *udev; struct urb *read_urb; struct usb_endpoint_descriptor *epin; struct urb *write_urb; struct usb_endpoint_descriptor *epout; char *read_buffer; char *write_buffer; int buffer_size; struct usb_anchor read_submitted; struct usb_anchor write_submitted; struct task_struct *thread; rx_submit_fn rx_submit; adma_callback adma_tx_callback; sdma_callback sdma_tx_callback; void *rx_data; void *tx_data; int state; int ep_mps; int max_packet_count; struct semaphore sem; int is_dloader; int sent_packet_count; int req_tx_packet; wait_queue_head_t rx_wait; wait_queue_head_t tx_wait; struct tasklet_struct tasklet; struct list_head rx_urb_list; struct list_head tx_urb_list; struct list_head rx_done_urb_list; struct list_head suspend_urb_list; spinlock_t rx_urb_lock; spinlock_t tx_urb_lock; int tx_urb_count; int rx_packet_count; int suspend; u64 tx_done_time; u64 rx_done_time; } *usb_ports[MAX_USB_PORT]; static int modem_assert(void); static int skw_recovery_mode(void); static struct usb_port_struct *log_port; int skw_reset_bus_dev(void); extern void kernel_restart(char *cmd); static int bulkin_read_timeout(int portno, char *buffer, int size, int *actual, int timeout); static int bulkout_write_timeout(int portno, char *buffer, int size, int *actual, int timeout); static void bulkout_async_complete(struct urb *urb); static void bulkin_async_complete(struct urb *urb); static int assert_info_print; static int usb_bt_rx_entry(void *para); char firmware_version[256]; #ifdef CONFIG_BT_SEEKWAVE static int bt_audio_port; #endif static struct platform_device *bluetooth_pdev = NULL; static int wifi_port_share; static int bulk_async_read; static int dump_memory_done; static char* dump_memory_buffer=NULL; static int dump_buffer_size=0; static int* dump_log_size=NULL; static int usb_bus_num; static int usb_port_num; void skw_get_port_statistic(char *buffer, int size) { int ret = 0; int i; if(!buffer) return; ret += sprintf(&buffer[ret], "%s", firmware_version); for(i=0; i= size) break; if (usb_ports[i]) { ret += sprintf(&buffer[ret], "port%d: req_tx %d tx_done %d, rx %d: tx_time: 0x%x rx_time: 0x%x\n", i, usb_ports[i]->req_tx_packet, usb_ports[i]->sent_packet_count, usb_ports[i]->rx_packet_count, (u32)usb_ports[i]->tx_done_time, (u32)usb_ports[i]->rx_done_time); } } } #include "usb_boot.c" void modem_register_notify(struct notifier_block *nb) { blocking_notifier_chain_register(&modem_notifier_list, nb); } void modem_unregister_notify(struct notifier_block *nb) { blocking_notifier_chain_unregister(&modem_notifier_list, nb); } void modem_notify_event(int event) { blocking_notifier_call_chain(&modem_notifier_list, event, NULL); } void skw_usb_exception_work(struct work_struct *work) { struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); skw_usb_info(" enter cp_state=%d...\n", recovery->cp_state); mutex_lock(&recovery->except_mutex); if(recovery->cp_state!=1) { mutex_unlock(&recovery->except_mutex); return; } recovery->cp_state = DEVICE_BLOCKED_EVENT; mutex_unlock(&recovery->except_mutex); modem_notify_event(DEVICE_BLOCKED_EVENT); service_state_map=0; skw_recovery_mode(); } int skw_usb_recovery_debug(int disable) { cls_recovery_mode_en = disable; skw_usb_info("the recovery status =%d\n", cls_recovery_mode_en); return 0; } int skw_usb_recovery_debug_status(void) { skw_usb_info("the recovery val =%d\n", cls_recovery_mode_en); return cls_recovery_mode_en; } static void usb_setup_service_devices(void) { #ifdef CONFIG_BT_SEEKWAVE struct usb_port_struct *bt_port; #endif int ret; skw_bind_boot_driver(&usb_ports[0]->udev->dev); if(usb_ports[1]->pdev){ if(wifi_data_pdev==NULL) { wifi_data_pdev = usb_ports[1]->pdev; ret = platform_device_add(usb_ports[1]->pdev); if(ret) { skw_usb_err("the fail to register WIFI device\n"); wifi_data_pdev = NULL; platform_device_put(usb_ports[1]->pdev); } else { skw_usb_info("add WIFI devices done\n"); } } } else skw_usb_err("NOT suppport WIFI service\n"); #ifdef CONFIG_BT_SEEKWAVE if (bluetooth_pdev) { bt_port = usb_ports[bt_audio_port]; bt_port->pdev = bluetooth_pdev; bluetooth_pdev = NULL; ret = platform_device_add(bt_port->pdev); if(ret) { skw_usb_err("failt to register Bluetooth device\n"); platform_device_put(bt_port->pdev); bt_port->pdev = NULL; } else skw_usb_info("add Bluetooth devices done\n"); } #endif } void add_devices_work(struct work_struct *work) { if (usb_ports[0]) usb_setup_service_devices(); } void skw_set_bt_suspend_flag(void) { } static void usb_port_alloc_recv_urbs(struct usb_port_struct *port, struct usb_endpoint_descriptor *epd, int count, int buffer_size) { int i; struct urb *urb; for(i=0; itransfer_buffer = NULL; urb->transfer_buffer_length = 0; } else{ urb->transfer_buffer = kzalloc(buffer_size, GFP_KERNEL); if(!urb->transfer_buffer) { usb_free_urb(urb); break; } urb->transfer_buffer_length = buffer_size; } usb_fill_bulk_urb(urb, port->udev,usb_rcvbulkpipe(port->udev, epd->bEndpointAddress), urb->transfer_buffer, buffer_size, bulkin_async_complete, NULL); list_add_tail(&urb->urb_list, &port->rx_urb_list); } skw_usb_dbg(" urb cout %d\n", i); } static void usb_port_alloc_xmit_urbs(struct usb_port_struct *port, struct usb_endpoint_descriptor *epd, int count, int buffer_size) { int i; struct urb *urb; for(i=0; itransfer_buffer = NULL; urb->transfer_buffer_length = 0; } else{ urb->transfer_buffer = kzalloc(buffer_size, GFP_KERNEL); if(!urb->transfer_buffer) { usb_free_urb(urb); break; } urb->transfer_buffer_length = buffer_size; } usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, epd->bEndpointAddress), urb->transfer_buffer, buffer_size, bulkout_async_complete, NULL); list_add_tail(&urb->urb_list, &port->tx_urb_list); } skw_usb_dbg(" urb cout %d\n", i); } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ int open_usb_port(int id, void *callback, void *data) { struct usb_port_struct *port; if(id >= MAX_USB_PORT) return -EINVAL; port = usb_ports[id]; if(port->state==0) return -EIO; skw_usb_info("port%d\n", id); if (port->state==1) { if(port->read_urb && !port->read_urb->context) init_usb_anchor(&port->read_submitted); if(port->write_urb && !port->write_urb->context) init_usb_anchor(&port->write_submitted); } port->state = 2; port->rx_submit = callback; port->rx_data = data; if(callback && data && !port->thread) { sema_init(&port->sem, 0); port->thread = kthread_create(usb_bt_rx_entry, port, port->interface->cur_altsetting->string); if(port->thread) wake_up_process(port->thread); } if (port->interface && modem_status==MODEM_ON) { struct usb_host_interface *iface_desc; iface_desc = port->interface->cur_altsetting; if (iface_desc && iface_desc->string && !strncmp(iface_desc->string, "LOG", 3)) skw_usb_cp_log(0); } return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int bulkin_read(struct usb_port_struct *port, void *buffer, int size) { int retval = -1; DECLARE_COMPLETION_ONSTACK(done); if(port->state==0) return -EIO; if(port == log_port){ memset(buffer, 0 , size); } if(port->read_urb) { port->read_urb->transfer_buffer = buffer; port->read_urb->transfer_buffer_length = size; port->read_urb->context = &done; usb_anchor_urb(port->read_urb, &port->read_submitted); retval = usb_submit_urb(port->read_urb,GFP_KERNEL); if(retval==0) { retval = wait_for_completion_interruptible(&done); if(retval == -ERESTARTSYS) usb_kill_urb(port->read_urb); else if(port->read_urb->status) retval = port->read_urb->status; else if(retval==0) retval = port->read_urb->actual_length; port->read_urb->context = NULL; } else { if (retval < 0) usb_unanchor_urb(port->read_urb); port->read_urb->context = NULL; } } if(port == log_port) { if(assert_info_print && assert_info_print<28 && retval<100) { assert_info_print++; if(retval > 4) skw_usb_info("%s", (char *)buffer); } if(retval == 4) assert_info_print = 28; } return retval; } int skw_bus_version(void) { skw_usb_info("USB bus Version1.0\n"); return 0; } /******************************************************* * bulkin_read_async - async read from bulk in endpoint * @port: pointer to struct usb_port_struct * * Submit a read urb for bulk in endpoint asynchronously. * * Return: 0 on success, negative value on error ******************************************************/ int bulkin_read_async(struct usb_port_struct *port) { int retval = -1; unsigned long flags; struct urb *urb; spin_lock_irqsave(&port->rx_urb_lock, flags); urb = list_first_entry(&port->rx_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); spin_unlock_irqrestore(&port->rx_urb_lock, flags); if(urb->context) { skw_usb_info("port is busy!!!\n"); return -EBUSY; } urb->complete = bulkin_async_complete; urb->context = port; if (port->suspend) list_add_tail(&urb->urb_list, &port->suspend_urb_list); else { usb_anchor_urb(urb, &port->read_submitted); bulk_async_read++; retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval < 0) { bulk_async_read--; usb_unanchor_urb(urb); urb->context = NULL; skw_usb_info(" is error!!! %d\n", retval); list_add_tail(&urb->urb_list, &port->suspend_urb_list); } } return retval; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int bulkout_write(struct usb_port_struct *port, void *buffer, int size) { int retval = -1; DECLARE_COMPLETION_ONSTACK(done); if (port && port->write_urb && !port->write_urb->context) { if (port->suspend) { skw_usb_info("port%d is suspended\n", port->portno); return -EOPNOTSUPP; } port->write_urb->context = &done; port->write_urb->transfer_buffer = buffer; port->write_urb->transfer_buffer_length = size; if(size%port->ep_mps == 0) port->write_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(port->write_urb, &port->write_submitted); retval = usb_submit_urb(port->write_urb,GFP_KERNEL); if(retval==0) { retval = wait_for_completion_interruptible(&done); if(retval==-ERESTARTSYS) usb_kill_urb(port->write_urb); else if (port->write_urb->status) retval = port->write_urb->status; else retval = port->write_urb->actual_length; port->write_urb->context = NULL; } else { if (retval < 0) { usb_unanchor_urb(port->write_urb); skw_usb_info("is error!!! %d\n", retval); } port->write_urb->context = NULL; } } return retval; } int bulkout_write_async(struct usb_port_struct *port, void *buffer, int size) { int retval = -1; struct urb *urb; unsigned long flags; if (port->suspend) { skw_usb_info("port%d is suspended\n", port->portno); return -EOPNOTSUPP; } spin_lock_irqsave(&port->tx_urb_lock, flags); if(list_empty(&port->tx_urb_list)) { spin_unlock_irqrestore(&port->tx_urb_lock, flags); retval = wait_event_interruptible(port->tx_wait, (!list_empty(&port->tx_urb_list))); spin_lock_irqsave(&port->tx_urb_lock, flags); } urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); port->tx_urb_count++; spin_unlock_irqrestore(&port->tx_urb_lock, flags); usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress), buffer, size, bulkout_async_complete, port); if(size%port->ep_mps == 0) urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &port->write_submitted); retval = usb_submit_urb(urb,GFP_KERNEL); if (retval < 0) { usb_unanchor_urb(urb); spin_lock_irqsave(&port->tx_urb_lock, flags); port->tx_urb_count--; list_add_tail(&urb->urb_list, &port->tx_urb_list); spin_unlock_irqrestore(&port->tx_urb_lock, flags); skw_usb_info("is error!!! %d\n", retval); } skw_usb_dbg(" portno %d wait done %d %d\n", port->portno, retval, port->tx_urb_count); return retval; } void check_sgs_headers(struct scatterlist *sgs, int sg_num, int total) { int i,size; struct skw_packet2_header *header; size = 0; for (i=0; ilen; if (header->len > 2048 || size > total) skw_usb_err("invalid packet: (%d - %d):( %d-%d-%d)\n", total, sg_num, i, header->len, size); } } int bulkout_write_sg_async(struct usb_port_struct *port, struct scatterlist *sgs, int sg_num, int total) { struct urb *urb; unsigned long flags; int retval = -1; check_sgs_headers(sgs, sg_num, total); spin_lock_irqsave(&port->tx_urb_lock, flags); if(list_empty(&port->tx_urb_list)) { spin_unlock_irqrestore(&port->tx_urb_lock, flags); retval = wait_event_interruptible(port->tx_wait, (!list_empty(&port->tx_urb_list))); spin_lock_irqsave(&port->tx_urb_lock, flags); } if (port->state==0) return -EIO; urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); port->tx_urb_count++; list_del_init(&urb->urb_list); port->req_tx_packet += sg_num; spin_unlock_irqrestore(&port->tx_urb_lock, flags); urb->transfer_buffer = NULL; urb->transfer_buffer_length = 0; usb_fill_bulk_urb(urb, port->udev,usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress), NULL, 0, bulkout_async_complete, port); urb->sg = sgs; urb->num_sgs = sg_num; urb->transfer_buffer_length = total; if(total%port->ep_mps == 0) urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &port->write_submitted); //skw_usb_info("portno %d submit %d\n", port->portno, port->tx_urb_count); retval = usb_submit_urb(urb,GFP_KERNEL); if (retval < 0) { usb_unanchor_urb(urb); spin_lock_irqsave(&port->tx_urb_lock, flags); port->tx_urb_count--; list_add_tail(&urb->urb_list, &port->tx_urb_list); spin_unlock_irqrestore(&port->tx_urb_lock, flags); } return retval; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int bulkout_write_sg(struct usb_port_struct *port, struct scatterlist *sgs, int sg_num, int total) { int retval = -1; DECLARE_COMPLETION_ONSTACK(done); if(!port->write_urb) return -ENODEV; if(port->write_urb->context) { skw_usb_info("port is busy!!!\n"); return -EBUSY; } if(port->write_urb) { port->write_urb->sg = sgs; port->write_urb->num_sgs = sg_num; port->write_urb->transfer_buffer_length = total; if(total%port->ep_mps == 0) port->write_urb->transfer_flags |= URB_ZERO_PACKET; port->write_urb->context = &done; port->req_tx_packet += port->write_urb->num_sgs; usb_anchor_urb(port->write_urb, &port->write_submitted); retval = usb_submit_urb(port->write_urb,GFP_KERNEL); if(retval==0) { retval = wait_for_completion_interruptible(&done); if(retval==0) retval = port->write_urb->actual_length; port->write_urb->context = NULL; port->sent_packet_count += sg_num; } else { skw_port_log(port->portno, "%s retval = %d\n", __func__, retval); usb_unanchor_urb(port->write_urb); port->write_urb->context = NULL; } } if(retval > 0) return 0; return retval; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int send_data(int portno, char *buffer, int total) { struct usb_port_struct *port; u32 *data; if(total==0) return 0; if (modem_status != MODEM_ON) return -EIO; port = usb_ports[portno]; if(!port || !port->state) return -EIO; if (port->suspend) { skw_usb_info("port%d is suspended\n", portno); return -EOPNOTSUPP; } if (portno == 0) { data = (u32 *)buffer; memcpy(last_sent_wifi_cmd, data, 12); last_sent_time = jiffies; last_sent_wifi_cmd[0] = bulk_async_read; } return bulkout_write(port, buffer, total); } static void check_first_header(char *buffer, int total) { struct skw_packet_header *header; header = (struct skw_packet_header *)buffer; if (header->len > 1536 || header->len > total) skw_usb_err("invalid packet: (%d - %d)\n", total, header->len); } static int send_data_async(int portno, char *buffer, int total) { struct usb_port_struct *port; int ret; if(total==0) return 0; if (modem_status != MODEM_ON) return -EIO; port = usb_ports[portno]; if(!port || !port->state) return -EIO; ret = bulkout_write_async(port, buffer, total); check_first_header(buffer, total); return ret; } int recv_data(int portno, char *buffer, int total) { struct usb_port_struct *port; if(total==0) return 0; port = usb_ports[portno]; if(!port || !port->state) return -EIO; return bulkin_read(port, buffer, total); } int close_usb_port(int portno) { struct usb_port_struct *port; port = usb_ports[portno]; skw_usb_info("port%d\n", portno); if (port) { port->state = 1; if(port->write_urb && port->write_urb->context) usb_kill_urb(port->write_urb); if(port->read_urb && port->read_urb->context) usb_kill_urb(port->read_urb); if(port->thread && down_interruptible(&port->sem)) skw_usb_info("port%d rx thread exit\n", portno); port->thread = NULL; if (port->interface) { struct usb_host_interface *iface_desc; iface_desc = port->interface->cur_altsetting; if (iface_desc && iface_desc->string && !strncmp(iface_desc->string, "LOG", 3)) skw_usb_cp_log(1); } } return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ int wifi_send_cmd(int portno, struct scatterlist *sg, int sg_num, int total) { struct usb_port_struct *port; u32 *data; int ret; if(total==0) return 0; if (modem_status != MODEM_ON) return -EIO; if(portno >= MAX_USB_PORT) return -EINVAL; port = usb_ports[portno]; if(!port || !port->state) return -EIO; if (port->suspend) { skw_usb_info("port%d is suspended\n", portno); return -EOPNOTSUPP; } if (portno == 0) { data = (u32 *)sg_virt(sg); memcpy(last_sent_wifi_cmd, data, 12); last_sent_time = jiffies; last_sent_wifi_cmd[0] = bulk_async_read; } ret = bulkout_write_sg(port, sg, sg_num, total); return ret; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ int wifi_send_cmd_async(int portno, struct scatterlist *sg, int sg_num, int total) { struct usb_port_struct *port; u32 *data; if(total==0) return 0; if (modem_status != MODEM_ON) return -EIO; if(portno >= MAX_USB_PORT) return -EINVAL; port = usb_ports[portno]; if(!port || !port->state) return -EIO; if (port->suspend) { skw_usb_info("port%d is suspended\n", portno); return -EOPNOTSUPP; } if (portno == 0) { data = (u32 *)sg_virt(sg); memcpy(last_sent_wifi_cmd, data, 12); last_sent_time = jiffies; last_sent_wifi_cmd[0] = bulk_async_read; } return bulkout_write_sg_async(port, sg, sg_num, total); } /************************************************************************ *Decription: manual assert modem *Author:jiayong.yang *Date:2021-08-03 *Modfiy: *Notes: this function must not be invoked in IRQ context. ************************************************************************/ static int modem_assert_work(void) { struct usb_port_struct *port; struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); int ret = -1; u32 *cmd = last_sent_wifi_cmd; if(modem_status == MODEM_HALT){ skw_usb_info("modem in recovery mode \n"); return 0; } port = usb_ports[0]; if(port && port->state) { recovery->cp_state =1; ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), VENDOR_MSG_MODEM_ASSERT, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, 0,0,NULL,0,1000); skw_usb_err("SND ASSERT CMD ret = %d cmd: 0x%x 0x%x 0x%x: ACK 0x%x-0x%x-0x%x EVT: 0x%x 0x%x 0x%x \n", ret, cmd[0], cmd[1], cmd[2], last_recv_wifi_ack[0],last_recv_wifi_ack[1],last_recv_wifi_ack[2], last_recv_wifi_evt[0],last_recv_wifi_evt[1],last_recv_wifi_evt[2]); modem_status = MODEM_HALT; #ifdef CONFIG_SEEKWAVE_PLD_RELEASE schedule_delayed_work(&skw_except_work , msecs_to_jiffies(2000)); #else schedule_delayed_work(&skw_except_work , msecs_to_jiffies(6000)); #endif } return ret; } static void usb_control_work(struct work_struct *work) { modem_assert_work(); } static int modem_assert(void) { struct usb_port_struct *port; port = usb_ports[0]; if (port) schedule_work(&usb_control_worker); return 0; } int wifi_service_start(void) { int ret = 0; if(!usb_boot_data) return -ENODEV; ret=usb_boot_data->wifi_start(); return ret; } int wifi_service_stop(void) { int ret = 0; if(!usb_boot_data) return -ENODEV; ret=usb_boot_data->wifi_stop(); return ret; } int bt_service_start(void) { int ret = 0; if(!usb_boot_data) return -ENODEV; ret=usb_boot_data->bt_start(); return ret; } int bt_service_stop(void) { int ret = 0; if(!usb_boot_data) return -ENODEV; ret=usb_boot_data->bt_stop(); return ret; } static int send_modem_service_command(u16 service, u16 command) { struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); struct usb_port_struct *port; int ret = -1; int timeout = 1000; port = usb_ports[1]; #ifndef MODEM_WDT_SUPPORT if(usb_boot_data->chip_en < 0){ skw_usb_err("chip_en = %d Invalid Pls check HW !!\n", usb_boot_data->chip_en); return ret; } #endif if(port) skw_usb_info("(%d,%d) cp_state= %d\n", service, command, recovery->cp_state); if (recovery->cp_state) return ret; if(port && port->state) { skw_reinit_completion(download_done); ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), VENDOR_MSG_SERVICE_CTRL, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, service, command, NULL, 0, 1000); } if((command & 0x01) == SERVICE_START) { skw_usb_info("ret = %d\n", ret); complete(&loop_completion); start_service_flag = 1; wait_for_completion_interruptible_timeout(&download_done, msecs_to_jiffies(timeout + 1000*service)); service_state_map |= (1<state) { ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), VENDOR_MSG_PACKET_COUNT, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, portno, 0, port->read_buffer, size, 1000); packet_count = (u16 *)port->read_buffer; if(ret < 0) skw_port_log(portno,"%s (%d,%d) ret = %d\n", __func__, portno, *packet_count, ret); if(ret==size) port->max_packet_count = *packet_count; else port->max_packet_count = MAX_PACKET_COUNT; } return ret; } void skw_usb_cp_log(int disable) { struct usb_port_struct *port; int ret = -1; port = usb_ports[0]; if(port && port->state) { ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), VENDOR_MSG_LOG_SWITCH, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, disable, 0, NULL, 0, 10); skw_usb_info("(disable=%d) ret = %d\n", disable, ret); } cp_log_status = disable; } /************************************************************************ *Decription:send BT start command to modem. *Author:jiayong.yang *Date:2021-08-30 *Modfiy: * ********************************************************************* */ static int skw_BT_service_start(void) { u16 cmd = SERVICE_START; if (!wifi_data_pdev) return -ENODEV; skw_usb_info("Enter modem_status=%d\n", modem_status); if (service_state_map & (1<write_urb && port->write_urb->context) { usb_kill_anchored_urbs(&port->write_submitted); } else if (port && port->tx_urb_count) { usb_kill_anchored_urbs(&port->write_submitted); } } if (modem_status == MODEM_HALT) { service_state_map &= ~(1<pipe) - 1; port = usb_ports[portno]; port->rx_done_time = jiffies; port->rx_packet_count++; if(urb) { if(urb->status) { skw_usb_info("endpoint%d actual = %d status %d\n", usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); } if(urb->status == -ENOENT && port && port!=log_port && port->suspend) list_add_tail(&urb->urb_list, &port->suspend_urb_list); else if (urb->context) complete(urb->context); } } static void bulkin_async_complete(struct urb *urb) { struct usb_port_struct *port; if(!urb) return; port = urb->context; if(!port) return; port->rx_done_time = jiffies; bulk_async_read--; if(urb->status) { skw_usb_info("endpoint%d actual = %d status %d\n", usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); } if(urb->status == -ENOENT && port && port->suspend) list_add_tail(&urb->urb_list, &port->suspend_urb_list); else if (port) { urb->context = NULL; spin_lock(&port->rx_urb_lock); if (port->state) list_add_tail(&urb->urb_list, &port->rx_done_urb_list); else list_add_tail(&urb->urb_list, &port->rx_urb_list); spin_unlock(&port->rx_urb_lock); if (port->state) tasklet_hi_schedule(&port->tasklet); } } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static void bulkout_complete(struct urb *urb) { struct usb_port_struct *port; int portno; portno = usb_pipeendpoint(urb->pipe) - 1; port = usb_ports[portno]; if(urb->status) skw_usb_info("endpoint%d actual = %d status %d\n", usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); if (port) { port->tx_done_time = jiffies; port->sent_packet_count++; } if (urb->context) complete(urb->context); } static void bulkout_async_complete(struct urb *urb) { struct usb_port_struct *port = urb->context; if(urb->status) { port->sent_packet_count += urb->num_sgs; if(urb->sg && port->adma_tx_callback) port->adma_tx_callback(port->portno, urb->sg, urb->num_sgs, port->tx_data, urb->status); else if(urb->transfer_buffer && port->sdma_tx_callback) port->sdma_tx_callback(port->portno, urb->transfer_buffer, urb->transfer_buffer_length, port->tx_data, urb->status); skw_usb_info("port%d endpoint%d actual = %d status %d\n", port->portno, usb_pipeendpoint(urb->pipe), urb->actual_length, urb->status); } else if(urb->sg && port->adma_tx_callback) { port->adma_tx_callback(port->portno, urb->sg, urb->num_sgs, port->tx_data, 0); port->sent_packet_count += urb->num_sgs; } else if(urb->transfer_buffer && port->sdma_tx_callback) port->sdma_tx_callback(port->portno, urb->transfer_buffer, urb->transfer_buffer_length, port->tx_data, 0); urb->context = NULL; port->tx_done_time = jiffies; spin_lock(&port->tx_urb_lock); list_add_tail(&urb->urb_list, &port->tx_urb_list); port->tx_urb_count--; if(port->tx_urb_count==0 && port->sent_packet_count!=port->req_tx_packet) skw_usb_info(" port[%d]= %d %d\n", port->portno, port->sent_packet_count, port->req_tx_packet); spin_unlock(&port->tx_urb_lock); wake_up_interruptible(&port->tx_wait); } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ int bulkin_read_timeout(int portno, char *buffer, int size, int *actual, int timeout) { struct usb_port_struct *port; unsigned int pipe; int ret; if(portno >= MAX_USB_PORT || !buffer || !size) return -EINVAL; port = usb_ports[portno]; if(!port->state) return -EIO; if(actual) *actual = 0; pipe = usb_rcvbulkpipe(port->udev, port->epin->bEndpointAddress); ret = usb_bulk_msg(port->udev, pipe, buffer, size, actual,timeout); if(port == log_port && actual) { if(assert_info_print && assert_info_print<28 && *actual<100) { assert_info_print++; if(*actual > 4) skw_usb_info("%s", (char *)buffer); } if(*actual == 4) assert_info_print = 28; } if(ret) return ret; if(actual) return *actual; return ret; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ int bulkout_write_timeout(int portno, char *buffer, int size, int *actual, int timeout) { struct usb_port_struct *port; unsigned int pipe; int ret; if(portno >= MAX_USB_PORT || !buffer || !size) return -EINVAL; port = usb_ports[portno]; if(!port->state) return -EIO; if(actual) *actual = 0; pipe = usb_sndbulkpipe(port->udev, port->epout->bEndpointAddress); ret = usb_bulk_msg(port->udev, pipe, buffer, size, actual,timeout); if(ret) return ret; if(actual) return *actual; return ret; } static void kick_rx_thread(void) { struct usb_port_struct *port; skw_usb_info("submitted urb %d\n", bulk_async_read); port = usb_ports[1]; if ((bulk_async_read == 0) && port && (!list_empty(&port->rx_urb_list))) bulkin_read_async(port); else if (port && list_empty(&port->rx_urb_list)) skw_usb_info("urb list is empty \n"); } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int register_rx_callback(int id, void *func, void *para); static int register_tx_callback(int id, void *func, void *para); static struct sv6160_platform_data wifi_pdata = { .data_port = 0, .cmd_port = 1, #ifdef CONFIG_SEEKWAVE_PLD_RELEASE .bus_type = USB_LINK|TX_SDMA|RX_SDMA|TX_ASYN|CP_RLS, #else .bus_type = USB_LINK|TX_SDMA|RX_SDMA|TX_ASYN|CP_DBG, #endif .max_buffer_size = MAX_BUFFER_SIZE, .align_value = 512, .hw_adma_tx = wifi_send_cmd, .hw_sdma_tx = send_data, .hw_adma_tx_async = wifi_send_cmd_async, .hw_sdma_tx_async = send_data_async, .callback_register = register_rx_callback, .modem_assert = modem_assert, .service_start = wifi_service_start, .service_stop = wifi_service_stop, .modem_register_notify = modem_register_notify, .modem_unregister_notify = modem_unregister_notify, .at_ops = { .port = 2, .open = open_usb_port, .close = close_usb_port, .read = recv_data, .write = send_data, .read_tm = bulkin_read_timeout, .write_tm = bulkout_write_timeout, }, .tx_callback_register = register_tx_callback, .rx_thread_wakeup = kick_rx_thread, }; void usb_handle(unsigned long tsk_data) { int size, read, ret; int transfer_count = 0, sg_count, offset; u16 data_flag = 0x8000; unsigned long flags; char *buffer; int *data; struct usb_port_struct *port = (struct usb_port_struct *) tsk_data; struct scatterlist *sg; struct urb *urb; u16 pcount, sequence; if (!strncmp(skw_chipid, "SV6316", 6) || !strlen(skw_chipid) || !strncmp(skw_chipid, "SV6160LITE", 10)) data_flag = 2; while(!list_empty(&port->rx_done_urb_list)) { if (!port->state || port!=usb_ports[1]) break; spin_lock_irqsave(&port->rx_urb_lock, flags); urb = list_first_entry(&port->rx_done_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); list_add_tail(&urb->urb_list, &port->rx_urb_list); spin_unlock_irqrestore(&port->rx_urb_lock, flags); sg_init_table(sgs, nr_sgs); read = urb->actual_length; buffer = urb->transfer_buffer; transfer_count++; if(urb->status < 0 || !port->state) { skw_usb_err(" bulkin read status=%d state=%d\n", urb->status, port->state); return ; } if(port->rx_submit) { int is_cmd; u32 d32; data = (int *)buffer; d32 = data[0]; sequence = d32 & 0xffff; pcount = d32 >> 16; pcount &= 0x7fff; offset = 0; sg_count = 0; sg = sgs; is_cmd = 0; if (d32 & 0x80000000) { if (transfer_index != sequence) skw_usb_info("data lost: recved %d, expect:%d\n", transfer_index,sequence); transfer_index = sequence + 1; } while (offset+12 < read) { sg_count++; if(sg_count > nr_sgs) { skw_usb_warn("packet count is overflow %d : %d : %d : %d!!!\n", offset, read, sg_count, nr_sgs); sg_count--; break; } size = data[2] >> 16; size += 3; size = size & 0xfffffffc; if(data[2] & data_flag) { if (sg_count > 1 && !is_cmd) size = -1; else is_cmd = 1; } if (size + offset > read || size > 2048 || size <= 12) { skw_usb_warn("Invalid packet size=%d: %d : %d :%d 0x%x:0x%x!!!\n", size, offset, read, sg_count, d32, data[2]); if (cls_recovery_mode_en) { print_hex_dump(KERN_ERR, "PACKET1::", 0, 16, 1, urb->transfer_buffer, offset+12, 1); modem_assert(); } if (sg_count > 0) sg_count--; break; } sg_set_buf(sg, &buffer[offset], size); sg++; offset += size; if (is_cmd) { if (modem_status != MODEM_ON) skw_usb_info("rx_submit(0x%x): command: 0x%x 0x%x: 0x%x 0x%x readlen=%d\n", (u32)jiffies, data[2], data[3], last_recv_wifi_ack[1], last_recv_wifi_ack[2], read); if ((data[3] & 0xff) == 0x10) { last_ack_time = jiffies; memcpy(last_recv_wifi_ack, &data[1], 12); } else memcpy(last_recv_wifi_evt, &data[1], 12); } data = (int *)&buffer[offset]; } if (d32 & 0x80000000) { if (sg_count != pcount) skw_usb_info("packet lost:%d %d\n", sg_count, pcount); } if(sg_count >15) skw_usb_info("rx_submit: port%d packet count %d\n", port->portno, sg_count); if(is_cmd) port = usb_ports[wifi_pdata.cmd_port]; else port = usb_ports[wifi_pdata.data_port]; if (port->rx_submit) port->rx_submit(port->portno, sgs, sg_count, port->rx_data); port->rx_packet_count += sg_count; if (modem_status != MODEM_ON) return ; port = usb_ports[wifi_pdata.data_port]; } } while(!list_empty(&port->rx_urb_list)) { if (port->state==0) break; ret = bulkin_read_async(port); } } /********************************************************************** *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * **********************************************************************/ int usb_port_async_entry(void *para) { struct usb_port_struct *port = para; struct sched_param param; unsigned long flags; //int size, read, ret; int ret; u16 mpc; struct urb *urb; u16 data_flag = 0x8000; if(port->portno == 0) { param.sched_priority = USB_RX_TASK_PRIO; #if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE sched_set_fifo_low(current); #else sched_setscheduler(current, SCHED_FIFO, ¶m); #endif } if(port->max_packet_count) mpc = port->max_packet_count; else mpc = 2; if (!strncmp(skw_chipid, "SV6316", 6) || !strlen(skw_chipid) || !strncmp(skw_chipid, "SV6160LITE", 10)) data_flag = 2; nr_sgs = mpc+1; sgs = kzalloc((nr_sgs)*sizeof(struct scatterlist), GFP_KERNEL); if (!sgs) return -ENOMEM; bulk_async_read = 0; if (mpc<=13) usb_port_alloc_recv_urbs(port, port->epin, 3, 20*1024); else usb_port_alloc_recv_urbs(port, port->epin, 3, 24*1024); usb_port_alloc_xmit_urbs(port, port->epout,10,0); msleep(300); transfer_index = 0; skw_usb_info(" port %d running packet %d %s 0x%x...\n",port->portno, mpc, skw_chipid, data_flag); if (!list_empty(&port->rx_urb_list)) { ret = bulkin_read_async(port); } wait_event_interruptible(port->rx_wait, (!port->state)); skw_usb_info(" port %d stoped\n", port->portno); msleep(50); kfree(sgs); if(port->write_urb) { usb_kill_anchored_urbs(&port->write_submitted); } if(port->read_urb) { usb_kill_anchored_urbs(&port->read_submitted); } if(port->write_urb && port->write_urb->context) wait_for_completion_interruptible(port->write_urb->context); spin_lock_irqsave(&port->rx_urb_lock, flags); while(!list_empty(&port->rx_urb_list)) { urb = list_first_entry(&port->rx_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); if(urb->transfer_buffer) kfree(urb->transfer_buffer); usb_free_urb(urb); } spin_unlock_irqrestore(&port->rx_urb_lock, flags); while(!list_empty(&port->tx_urb_list)) { urb = list_first_entry(&port->tx_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); usb_free_urb(urb); } up(&port->sem); return 0; } static void skw_usb_kill_wifi_threads(struct usb_port_struct *p) { int i; struct usb_port_struct *port; for(i=0; i<3; i++) { port = usb_ports[i]; if(port==NULL) break; if(port && port->thread) { port->state = 0; } } } static void skw_usb_dump_memory(char *buffer, int size, int *log_size) { if (log_port->state==2) return; if (size && buffer && log_size) { dump_memory_buffer = buffer; dump_buffer_size = size; dump_log_size = log_size; skw_usb_info("dump_memory : %p-%d\n", buffer, size); schedule_work(&dump_memory_worker); } } static void show_assert_context(void) { int read; int error_count; int total_size; int dump_memory_size = 0; if(log_port && log_port->state!=2) { char *buffer; buffer = kzalloc(1024, GFP_KERNEL); if (!buffer) return; open_usb_port(log_port->portno, 0, 0); dump_memory_done = 0; error_count=0; total_size = 0; do { if (modem_status == MODEM_ON) break; if (!dump_memory_done) read = bulkin_read_timeout(log_port->portno, buffer, 1024, &read, 10); if (read > 0) { if (total_size + read < dump_buffer_size) { memcpy(&dump_memory_buffer[total_size], buffer, read); dump_memory_size = total_size + read; } total_size += read; memset(buffer, 0, read); } if(read == 4 || read < 0) { break; } } while (assert_info_print<100 && !dump_memory_done); while (!dump_memory_done) { if (modem_status == MODEM_ON) break; read = bulkin_read_timeout(log_port->portno, buffer, 1024, &read, 10); if (read <= 0) { error_count++; skw_usb_info("%s read = %d : total %d done=%d\n", current->comm, read, total_size, dump_memory_done); if(error_count >1) break; } else { if (total_size + read < dump_buffer_size) { memcpy(&dump_memory_buffer[total_size], buffer, read); dump_memory_size = total_size + read; } total_size += read; } } close_usb_port(log_port->portno); skw_usb_info("dump memory size: %d buffer_size: %d\n", dump_memory_size, dump_buffer_size); if (dump_log_size) *dump_log_size = dump_memory_size; kfree(buffer); } } static void dump_memory_work(struct work_struct *work) { if(dump_log_size && *dump_log_size==0) { skw_usb_info(" running...\n"); show_assert_context(); } } int skw_pin_config(struct usb_port_struct *port, char *buffer) { int i; u32 val; int func_index = 0; int g_offset = 0; u32 pin_group_index = 0; u32 func_group_index = 0; u32 func_sel_val[2] = {0}; u32 func_sel_off[] = {PN_FUNC_SEL0_OFFSET, PN_FUNC_SEL1_OFFSET}; u32 *pin_val; u8 pin_off[PN_CNT] = {0}; char value_str[9]; char result_str[15]; //e.g. PN:10:12345678 char off_str[3]; pin_val = (u32 *)kzalloc(PN_CNT * sizeof(u32), GFP_KERNEL); while (g_offset < usb_boot_data->nv_mem_pnfg_size) { //1. pin offset pin_off[pin_group_index] = usb_boot_data->nv_mem_pnfg_data[g_offset]; //2. func_sel value val = usb_boot_data->nv_mem_pnfg_data[g_offset + 1]; func_sel_val[func_group_index] |= (val << (3 * func_index)) & (7 << (3 * func_index)); //3. pin config value pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 2] << BIT_PN_DSLP_EN_START) & \ GENMASK(BIT_PN_DSLP_EN_END, BIT_PN_DSLP_EN_START);//dslp_en pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 3] << BIT_DRV_STREN_START) & \ GENMASK(BIT_DRV_STREN_END, BIT_DRV_STREN_START);//drv_strength pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 4] << BIT_NORMAL_WP_START) & \ GENMASK(BIT_NORMAL_WP_END, BIT_NORMAL_WP_START);//normal:wpu/wpd pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 5] << BIT_SCHMITT_START) & \ GENMASK(BIT_SCHMITT_END, BIT_SCHMITT_START);//schmitt trigger enable pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 6] << BIT_SLP_WP_START) & \ GENMASK(BIT_SLP_WP_END, BIT_SLP_WP_START);//sleep:wpu/wpd pin_val[pin_group_index] |= ((u32)usb_boot_data->nv_mem_pnfg_data[g_offset + 7]) & \ GENMASK(BIT_SLEEP_IE_OE_END, BIT_SLEEP_IE_OE_START);//sleep:ie/oe g_offset += 8; // 1(offset) + 1(pin sel) + 6(pin config) func_index++; pin_group_index++; if (func_index == PN_FUNCSEL_ONEGRP_CNT){ func_group_index++; func_index = 0; } } //function select for (i = 0;i < sizeof(func_sel_off) / sizeof(u32);i++) { sprintf(value_str, "%08x", func_sel_val[i]); sprintf(off_str, "%02x", func_sel_off[i]); sprintf(result_str, "PN:%s:%s", off_str, value_str); skw_usb_dbg("func_sel[%d]:%s\n", i, result_str); memcpy(buffer+256, result_str, 14); bulkout_write(port, buffer+256, 14); } //pin config for (i = 0;i < PN_CNT;i++) { sprintf(value_str, "%08x", pin_val[i]); sprintf(off_str, "%02x", pin_off[i]); sprintf(result_str, "PN:%s:%s", off_str, value_str); skw_usb_dbg("pin_conf[%d]:%s\n", i, result_str); memcpy(buffer+256, result_str, 14); bulkout_write(port, buffer+256, 14); } kfree(pin_val); return 0; } static int usb_loopcheck_entry(void *para) { struct usb_port_struct *port = para; char *buffer; int read, size; int count= 0, timeout=300; struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); size = 512; buffer = kzalloc(size, GFP_KERNEL); schedule_delayed_work(&skw_except_work , msecs_to_jiffies(6000)); while(port->state && buffer){ read = 0; memset(buffer,0,512); do{ if(port->state==0) break; read = bulkin_read(port, buffer, 256); }while(!read); if (port->suspend) { msleep(500); continue; } if(read < 0 || !port->state) { skw_usb_err("bulkin read_len=%d\n",read); break; } if(strncmp(buffer, "BSPREADY", read)) skw_usb_info("recv(%d):%s %s\n", read, skw_chipid, buffer); memcpy(buffer+256, "LOOPCHECK", 9); if (read==8 && !strncmp(buffer, "BSPREADY", read)) { if (start_service_flag) continue; bulkout_write(port, buffer+256, 9); //bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); } else if (read==9 && !strncmp(buffer, "WIFIREADY", read)) { start_service_flag = 0; service_state_map |= (1<portno, buffer+256,9, &size, 300); } else if (read==6 && !strncmp(buffer, "BTEXIT", read)) { complete(&download_done); //bulkout_write(port, buffer+256, 9); bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); } else if (read==7 && !strncmp(buffer, "BTREADY", read)) { start_service_flag = 0; service_state_map |= (1<portno, buffer+256,9, &size, 300); } else if (!strncmp(buffer, "BSPASSERT", 9)) { sprintf(firmware_version, "%s\n%s\n", firmware_version, buffer); skw_usb_err("cmd:0x%x 0x%x 0x%x ack:%x %x:%x event:0x%x:0x%x:0x%x time:0x%x:0x%x:0x%x\n", last_sent_wifi_cmd[0],last_sent_wifi_cmd[1],last_sent_wifi_cmd[2], last_recv_wifi_ack[0],last_recv_wifi_ack[1],last_recv_wifi_ack[2], last_recv_wifi_evt[0],last_recv_wifi_evt[1],last_recv_wifi_evt[2], (u32)jiffies,(u32)last_sent_time, (u32)last_ack_time); if(recovery->cp_state==1) cancel_delayed_work_sync(&skw_except_work); mutex_lock(&recovery->except_mutex); if(recovery->cp_state==DEVICE_BLOCKED_EVENT){ mutex_unlock(&recovery->except_mutex); break; } recovery->cp_state = 1; mutex_unlock(&recovery->except_mutex); assert_info_print = 1; memset(buffer, 0, read); skw_usb_kill_wifi_threads(port); modem_status = MODEM_HALT; modem_notify_event(DEVICE_ASSERT_EVENT); if (log_port->state!=2) schedule_work(&dump_memory_worker); memset(buffer, 0, 256); read = bulkin_read_timeout(port->portno, buffer, 256, &read, 1000); if (read > 0) skw_usb_info("bspassert after recv(%d): %s\n", read, buffer); dump_memory_done = 1; modem_notify_event(DEVICE_DUMPDONE_EVENT); msleep(100); skw_recovery_mode(); service_state_map =0; break; } else if (!strncmp("trunk_W", buffer, 7)) { #ifdef CONFIG_SKW_DL_TIME_STATS last_time = ktime_get(); skw_usb_info(",the download time start time %llu and lasttime %llu ,lose_time=%llu\n", cur_time, last_time,(last_time-cur_time)); #endif cancel_delayed_work_sync(&skw_except_work); recovery->cp_state = 0; assert_info_print = 0; modem_status = MODEM_ON; memset(firmware_version, 0 , sizeof(firmware_version)); strncpy(firmware_version, buffer, read); modem_notify_event(DEVICE_BSPREADY_EVENT); count = 0; service_state_map =0; //usb_setup_service_devices(); schedule_work(&add_device_work); bulkout_write_timeout(port->portno, buffer+256,9, &size, 300); if (usb_boot_data->nv_mem_pnfg_data != NULL && usb_boot_data->nv_mem_pnfg_size != 0) { skw_usb_info("UPDATE '%s' PINCFG from %s\n", skw_chipid, usb_boot_data->skw_nv_name); skw_pin_config(port, buffer); } } wait_for_completion_interruptible_timeout(&loop_completion, msecs_to_jiffies(timeout)); skw_reinit_completion(loop_completion); } skw_usb_info(" -port%d is stopped\n", port->portno); if(port->read_urb && port->read_urb->context) { usb_kill_anchored_urbs(&port->read_submitted); } if(port->write_urb && port->write_urb->context) { usb_kill_anchored_urbs(&port->write_submitted); if(port->write_urb->context) wait_for_completion_interruptible(port->write_urb->context); } kfree(buffer); up(&port->sem); return 0; } static int usb_bt_rx_entry(void *para) { struct usb_port_struct *port = para; char *buffer; int read, size; size = 2048; buffer = kzalloc(size, GFP_KERNEL); while(port->state==2 && buffer){ read = 0; memset(buffer,0,size); do{ if(port->state != 2) break; read = bulkin_read(port, buffer, size); }while(!read); if(read < 0) { skw_usb_err("bulkin read_len=%d\n",read); break; } if(port->rx_submit) port->rx_submit(port->portno, port->rx_data, read, buffer); } skw_usb_info("-port%d is stopped\n", port->portno); if(port->write_urb && port->write_urb->context) { usb_kill_anchored_urbs(&port->write_submitted); } if(port->read_urb && port->read_urb->context) { usb_kill_anchored_urbs(&port->read_submitted); } if(buffer) kfree(buffer); up(&port->sem); return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static struct sv6160_platform_data ucom_pdata = { .max_buffer_size =0x800, .bus_type = USB_LINK, .hw_sdma_tx = send_data, .hw_sdma_rx = recv_data, .open_port = open_usb_port, .close_port = close_usb_port, .modem_assert = modem_assert, .service_start = bt_service_start, .service_stop = bt_service_stop, .modem_register_notify = modem_register_notify, .modem_unregister_notify = modem_unregister_notify, .dump_modem_memory = skw_usb_dump_memory, }; /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int register_rx_callback(int id, void *func, void *para) { if(id >= MAX_USB_PORT) return -EINVAL; if(usb_ports[id] == NULL) return -EIO; if(func && !usb_ports[id]->rx_submit) { usb_ports[id]->rx_submit = func; usb_ports[id]->rx_data = para; if(id==1) skw_WIFI_service_start(); return 0; } else if(!func && usb_ports[id]->rx_submit) { if(id==1) skw_WIFI_service_stop(); usb_ports[id]->rx_submit = func; usb_ports[id]->rx_data = para; return 0; } if(wifi_pdata.bus_type & TX_ASYN) { if(wifi_pdata.bus_type & TX_SDMA) usb_ports[id]->sdma_tx_callback = func; else usb_ports[id]->adma_tx_callback = func; } usb_ports[id]->tx_data = para; return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int register_tx_callback(int id, void *func, void *para) { if(id >= MAX_USB_PORT) return -EINVAL; if(usb_ports[id] == NULL) return -EIO; if(wifi_pdata.bus_type & TX_ASYN) { if(wifi_pdata.bus_type & TX_SDMA) usb_ports[id]->sdma_tx_callback = func; else usb_ports[id]->adma_tx_callback = func; } usb_ports[id]->tx_data = para; return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int skw_usb_io_probe(struct usb_interface *interface, const struct usb_device_id *id) { struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); struct usb_port_struct *port; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *epd; struct platform_device *pdev; struct usb_device *udev = interface_to_usbdev(interface); char pdev_name[32], names[32]; int i, ret, dloader=0; memset(names, 0 ,sizeof(names)); iface_desc = interface->cur_altsetting; if (iface_desc->string == NULL) return -EINVAL; sprintf(names, "%s", iface_desc->string); if (!strncmp(names, "Boot", 4)) dloader = 1; #ifndef CONFIG_SV6160_LITE_FPGA if ((udev->descriptor.idProduct != 0x6316) && !dloader && chip_en_gpio < 0 && modem_status == MODEM_OFF) { #ifndef MODEM_WDT_SUPPORT return -EINVAL; #endif } if (chip_en_gpio < 0) { skw_usb_warn(" descriptor.idProduct = 0x%x CHIP_ID= %.100s\n", udev->descriptor.idProduct,udev->product); } #endif port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; mutex_lock(&recovery->except_mutex); if (usb_bus_num == 0xff && usb_port_num == 0xff) { skw_usb_info("bus[%x].port[%d]\n", udev->bus->busnum, udev->portnum); usb_bus_num = udev->bus->busnum; usb_port_num = udev->portnum; } else if (usb_bus_num != udev->bus->busnum || usb_port_num != udev->portnum) { mutex_unlock(&recovery->except_mutex); if (port) kfree(port); return -EBUSY; } mutex_unlock(&recovery->except_mutex); pdev = NULL; if (!strncmp(names, "WIFITCMD", 8)) wifi_port_share = 1; usb_ports[iface_desc->desc.bInterfaceNumber] = port; INIT_LIST_HEAD(&port->rx_urb_list); INIT_LIST_HEAD(&port->tx_urb_list); INIT_LIST_HEAD(&port->rx_done_urb_list); INIT_LIST_HEAD(&port->suspend_urb_list); spin_lock_init(&port->rx_urb_lock); spin_lock_init(&port->tx_urb_lock); port->tx_urb_count = 0; init_waitqueue_head(&port->rx_wait); init_waitqueue_head(&port->tx_wait); if(dloader) dloader = 1; else if (iface_desc->desc.bInterfaceNumber == 1) { if (!strncmp(skw_chipid, "SV6160LITE", 10)) { #ifdef SV6621S_WIRELESS sprintf(pdev_name, "%s%d", SV6621S_WIRELESS, iface_desc->desc.bInterfaceNumber); #else sprintf(pdev_name, "%s%d", SV6160_WIRELESS, iface_desc->desc.bInterfaceNumber); #endif } else if (!strncmp(skw_chipid, "SV6160", 6)) { sprintf(pdev_name, "%s%d", SV6160_WIRELESS, iface_desc->desc.bInterfaceNumber); } else if (!strncmp(skw_chipid, "SV6316", 6)) { sprintf(pdev_name, "%s%d", SV6316_WIRELESS, iface_desc->desc.bInterfaceNumber); } else { skw_usb_err( "unknow chip id!!! pls check you porting code!! and the connect the seekwave!!\n"); if (skw_chipid) skw_usb_err("unknow chip id: %s\n", skw_chipid); sprintf(pdev_name, "%s%d", SV6316_WIRELESS, iface_desc->desc.bInterfaceNumber); } if(!wifi_data_pdev) pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); else pdev = wifi_data_pdev; if(!pdev) return -ENOMEM; } else { #ifdef CONFIG_BT_SEEKWAVE if (!strncmp(names, "DATA", 4)) { ucom_pdata.data_port = 0; } else if(!strncmp(names, "BTDATA", 6)) ucom_pdata.data_port = iface_desc->desc.bInterfaceNumber; else if(!strncmp(names, "BTCMD", 5)) ucom_pdata.cmd_port = iface_desc->desc.bInterfaceNumber; else if(!strncmp(names, "BTISOC", 6)) { ucom_pdata.audio_port = iface_desc->desc.bInterfaceNumber; sprintf(pdev_name, "%s", "btseekwave"); ucom_pdata.port_name = "BTHCI"; bluetooth_pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); if (!bluetooth_pdev) return -ENOMEM; bluetooth_pdev->dev.parent = &udev->dev; bluetooth_pdev->dev.dma_mask = &port_dmamask; bluetooth_pdev->dev.coherent_dma_mask = port_dmamask; bt_audio_port = iface_desc->desc.bInterfaceNumber; memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); ret = platform_device_add_data(bluetooth_pdev, &ucom_pdata, sizeof(ucom_pdata)); if(ret) { skw_usb_err("failed to add platform data \n"); platform_device_put(pdev); kfree(port); return ret; } skw_usb_info("add the bt devices \n"); }else if(!strncmp(names, "AUDIO", 5)) { ucom_pdata.audio_port = 0; sprintf(pdev_name, "%s", "btseekwave"); ucom_pdata.port_name = "BTHCI"; bluetooth_pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); if (!bluetooth_pdev) return -ENOMEM; bluetooth_pdev->dev.parent = &udev->dev; bluetooth_pdev->dev.dma_mask = &port_dmamask; bluetooth_pdev->dev.coherent_dma_mask = port_dmamask; bt_audio_port = iface_desc->desc.bInterfaceNumber; memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); ret = platform_device_add_data(bluetooth_pdev, &ucom_pdata, sizeof(ucom_pdata)); if(ret) { skw_usb_err("failed to add platform data \n"); platform_device_put(pdev); kfree(port); return ret; } } else #endif if (iface_desc->desc.bInterfaceNumber && strncmp(names, "LOOP", 4)) { sprintf(pdev_name, "%s", "skw_ucom"); ucom_pdata.port_name = iface_desc->string; ucom_pdata.data_port = iface_desc->desc.bInterfaceNumber; memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); pdev = platform_device_alloc(pdev_name, PLATFORM_DEVID_AUTO); if(!pdev) return -ENOMEM; } } if(!dloader) { if (1==iface_desc->desc.bInterfaceNumber && wifi_data_pdev) { struct sv6160_platform_data *pdata; pdev = wifi_data_pdev; //pdev->dev.parent = NULL; pdata = pdev->dev.platform_data; if (pdata) { pdata->align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; wifi_pdata.align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; } port->pdev = pdev; } else if (iface_desc->desc.bInterfaceNumber && pdev) { if (1==iface_desc->desc.bInterfaceNumber && usb_boot_data && usb_boot_data->pdev) { pdev->dev.parent = &usb_boot_data->pdev->dev; } else pdev->dev.parent = &udev->dev; pdev->dev.dma_mask = &port_dmamask; pdev->dev.coherent_dma_mask = port_dmamask; if(iface_desc->desc.bInterfaceNumber == 1) { wifi_pdata.align_value = iface_desc->endpoint[0].desc.wMaxPacketSize; if(usb_boot_data && usb_boot_data->iram_dl_size >0x50000) wifi_pdata.at_ops.port = 4; else wifi_pdata.at_ops.port = 2; if(udev->config->string && !strncmp(udev->config->string, "ECOM", 4)) { wifi_pdata.bus_type &= ~TYPE_MASK; wifi_pdata.bus_type |= USB2_LINK; } ret = platform_device_add_data(pdev, &wifi_pdata, sizeof(wifi_pdata)); modem_status = MODEM_ON; } else{ memcpy(ucom_pdata.chipid, skw_chipid, SKW_CHIP_ID_LENGTH); ret = platform_device_add_data(pdev, &ucom_pdata, sizeof(ucom_pdata)); } if(ret) { skw_usb_err("failed to add platform data \n"); platform_device_put(pdev); kfree(port); return ret; } if(iface_desc->desc.bInterfaceNumber>1){ ret = platform_device_add(pdev); if(ret) { skw_usb_err("failt to register platform device\n"); platform_device_put(pdev); kfree(port); return ret; } } port->pdev = pdev; } } usb_set_intfdata(interface, port); port->interface = usb_get_intf(interface); port->udev = usb_get_dev(udev); /* register struct wcn_usb_intf */ skw_usb_dbg("intf[%x] is registerred: ep count %d %s\n", iface_desc->desc.bInterfaceNumber, iface_desc->desc.bNumEndpoints, iface_desc->string); ret = -ENOMEM; for(i=0; idesc.bNumEndpoints; i++) { epd = &iface_desc->endpoint[i].desc; port->buffer_size = 5120; port->ep_mps = epd->wMaxPacketSize; if(usb_endpoint_is_bulk_in(epd)) { port->epin = epd; port->read_urb = usb_alloc_urb(0, GFP_KERNEL); if(!port->read_urb) goto err0; if(iface_desc->desc.bInterfaceNumber > 1) { port->read_buffer = NULL; port->buffer_size = 0; } else { port->read_buffer = kzalloc(port->buffer_size , GFP_KERNEL); if(!port->read_buffer) goto err0; } usb_fill_bulk_urb(port->read_urb, udev, usb_rcvbulkpipe(udev, epd->bEndpointAddress), port->read_buffer, port->buffer_size, bulkin_complete, port); port->read_urb->context = NULL; init_usb_anchor(&port->read_submitted); skw_usb_dbg("BulkinEP = 0x%x rp=%p\n", epd->bEndpointAddress, port->read_buffer); } else if(usb_endpoint_is_bulk_out(epd)) { port->epout = epd; port->write_urb = usb_alloc_urb(0, GFP_KERNEL); if(!port->write_urb) goto err0; if(iface_desc->desc.bInterfaceNumber > 1) { port->write_buffer = NULL; port->buffer_size = 0; } else{ port->write_buffer = kzalloc(port->buffer_size, GFP_KERNEL); if(!port->write_buffer) goto err0; } usb_fill_bulk_urb(port->write_urb, udev, usb_sndbulkpipe(udev, epd->bEndpointAddress), port->write_buffer, port->buffer_size, bulkout_complete,port); port->write_urb->context = NULL; init_usb_anchor(&port->write_submitted); skw_usb_dbg("BulkoutEP = 0x%x wp =%p context %p\n", epd->bEndpointAddress, port->write_buffer, port->write_urb->context); } } if(!dloader) { port->portno = iface_desc->desc.bInterfaceNumber; port->state = 1; if (port->portno<=1) { if (!strncmp(names, "WIFIDATA", 8)) { skw_get_packet_count(port->portno); wifi_pdata.cmd_port = 1 - port->portno; wifi_pdata.data_port = port->portno; port->thread = kthread_create(usb_port_async_entry, port, iface_desc->string); tasklet_init(&port->tasklet, usb_handle, (unsigned long) port); } else { wifi_pdata.cmd_port = port->portno; wifi_pdata.data_port = 1 - port->portno; } if(port->thread) { sema_init(&port->sem, 0); wake_up_process(port->thread); } else sema_init(&port->sem, 1); } else if(!strncmp(names, "LOOP", 4)) { sema_init(&port->sem, 0); port->thread = kthread_create(usb_loopcheck_entry, port, iface_desc->string); if (port->thread) wake_up_process(port->thread); } else sema_init(&port->sem, 1); } else { port->state = 1; assert_info_print = 0; INIT_WORK(&port->work, dloader_work); if (usb_boot_data && usb_boot_data->iram_dl_size && usb_boot_data->dram_dl_size) { skw_usb_info("schedule boot-work: 0x%x:0x%x\n", usb_boot_data->dram_dl_size,usb_boot_data->iram_dl_size); schedule_work(&port->work); modem_status = MODEM_ON; } port->is_dloader = 1; } if (!strncmp(names, "LOG", 3)) log_port = port; return 0; err0: skw_usb_err("no memory to register device\n"); if(port->write_buffer) kfree(port->write_buffer); if(port->read_buffer) kfree(port->read_buffer); if(port->write_urb) usb_free_urb(port->write_urb); if(port->read_urb) usb_free_urb(port->read_urb); if(port->pdev) platform_device_unregister(port->pdev); usb_ports[iface_desc->desc.bInterfaceNumber] = NULL; kfree(port); return ret; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int launch_download_work(char *data, int size,int addr) { int chk_ports =0; firmware_size = size;//link to usb_download size firmware_data = data;//link to usb_download dl_data firmware_addr = addr; do{ if((usb_ports[0] !=NULL)&&(usb_ports[0]->state)) { chk_ports = 1; break; } msleep(10); }while(!chk_ports); schedule_work(&usb_ports[0]->work); return 0; } static int skw_recovery_mode(void) { int ret=0; skw_usb_info("[+]\n"); if(!cls_recovery_mode_en){ ret = skw_reset_bus_dev(); } else { skw_usb_info("No Need To Recovery!\n"); } skw_usb_info("[-]\n"); return ret; } void get_bt_antenna_mode(char *mode) { } void reboot_to_change_bt_antenna_mode(char *mode) { } void get_USB_speed_mode(char *mode) { } void reboot_to_change_USB_speed_mode(char *mode) { } void reboot_to_change_bt_uart1(char *mode) { } static irqreturn_t skw_gpio_irq_handler(int irq, void *dev_id) { return IRQ_HANDLED; } /************************************************************************ *Decription: *Author:JUNWEI.JIANG *Date:2021-12-20 *Modfiy: * ********************************************************************* */ int skw_boot_loader(struct seekwave_device *boot_data) { int ret = 1; if (usb_ports[0] && usb_ports[0]->suspend) return -EOPNOTSUPP; usb_boot_data= boot_data; skw_usb_info("status:%d , chip_en_gpio=%d, gpio_in=%d", modem_status, usb_boot_data->chip_en, usb_boot_data->gpio_in); chip_en_gpio = usb_boot_data->chip_en; #ifdef CONFIG_SKW_DL_TIME_STATS cur_time = ktime_get(); #endif if (host_wake_gpio < 0 && usb_boot_data->gpio_in>=0) { int irq_num; host_wake_gpio = usb_boot_data->gpio_in; irq_num = gpio_to_irq(host_wake_gpio); ret = request_irq(irq_num, skw_gpio_irq_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, "skw-gpio-irq", NULL); skw_usb_info("request_gpio_irq ret=%d\n", ret); if (ret == 0) enable_irq_wake(irq_num); } if(!boot_data->first_dl_flag ){ if (usb_ports[0] && !usb_ports[0]->is_dloader) { //usb_setup_service_devices(); schedule_work(&add_device_work); } else if(boot_data->iram_img_data !=NULL && boot_data->dram_img_data!=NULL){ skw_usb_info("USB FIRST BOOT... \n"); ret=launch_download_work(boot_data->iram_img_data,boot_data->iram_dl_size,boot_data->iram_dl_addr); }else{ skw_usb_info("The CPBOOT not download from AP!!!!\n"); } } if(boot_data->dl_module==RECOVERY_BOOT){ skw_recovery_mode(); return 0; } if(boot_data->service_ops==SKW_WIFI_START){ //skw_WIFI_service_start(); //skw_usb_info("----WIFI-SERVICE-----START!!!\n"); }else if(boot_data->service_ops== SKW_WIFI_STOP && (service_state_map & (1<service_ops == SKW_BT_START){ skw_usb_info("----BT-SERVICE-----START!!!\n"); ret=skw_BT_service_start(); }else if(boot_data->service_ops==SKW_BT_STOP && (service_state_map & (1<state || !usb_ports[0]->udev){ skw_usb_err(" the port open device fail !!!\n"); return NULL; } return &usb_ports[0]->udev->dev; } /************************************************************************ *Decription:check dev ready for boot *Author:junwei.jiang *Date:2022-06-07 *Modfiy: * ********************************************************************* */ int skw_reset_bus_dev(void) { struct usb_port_struct *port; int ret = -1; if(chip_en_gpio >= 0) { skw_chip_power_reset(); return 0; } port = usb_ports[0]; if (port == NULL) return 0; ret = usb_control_msg(port->udev, usb_sndctrlpipe(port->udev, 0), VENDOR_MSG_MODEM_RESET, USB_DIR_OUT| USB_TYPE_VENDOR|USB_RECIP_DEVICE, 0,0,NULL,0,100); skw_usb_info("ret = %d\n", ret); if (ret == -ETIMEDOUT) { usb_reset_device(port->udev); } return ret; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int skw_usb_io_free_suspend_urbs(struct usb_interface *interface) { struct usb_port_struct *port; struct urb *urb; port = usb_get_intfdata(interface); port->suspend = 0; while(!list_empty(&port->suspend_urb_list)) { urb = list_first_entry(&port->suspend_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); if (!list_empty(&port->suspend_urb_list)) list_add_tail(&urb->urb_list, &port->rx_urb_list); else { urb->status = -EIO; urb->complete(urb); } } return 0; } static void skw_usb_io_disconnect(struct usb_interface *interface) { int infno = interface->cur_altsetting->desc.bInterfaceNumber; struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); struct usb_port_struct *port; unsigned long flags; struct urb *urb; port = usb_get_intfdata(interface); if(!port) return; log_port = NULL; port->state = 0; skw_usb_info("interface[%x] disconnected %d\n", infno, modem_status); if(!port->is_dloader) { if (infno > 1) platform_device_unregister(port->pdev); if (infno == 1) { wake_up_interruptible(&port->rx_wait); wake_up_interruptible(&port->tx_wait); } if (modem_status==MODEM_ON) { if(wifi_data_pdev && &port->udev->dev == wifi_data_pdev->dev.parent) { if(recovery->cp_state == 0) modem_notify_event(DEVICE_DISCONNECT_EVENT); platform_device_unregister(wifi_data_pdev); wifi_data_pdev = NULL; skw_usb_info("WIFI device disconnected1!!!\n"); } } if (port->pdev == wifi_data_pdev && port->suspend) { modem_notify_event(DEVICE_DISCONNECT_EVENT); tasklet_kill(&port->tasklet); } skw_usb_io_free_suspend_urbs(interface); if(port->read_urb && port->read_urb->context) usb_kill_anchored_urbs(&port->read_submitted); if(port->write_urb && port->write_urb->context) usb_kill_anchored_urbs(&port->write_submitted); if(port->thread && !port->suspend&& down_timeout(&port->sem, 1000)) skw_usb_info("start to unregister interface[%x]\n", infno); } else flush_work(&port->work); if(port->read_urb && !port->read_urb->context) { kfree(port->read_urb); port->read_urb = NULL; } else skw_usb_err(" memory leak port.r%d!!!!!!!!\n", infno); if(port->write_urb && !port->write_urb->context) { kfree(port->write_urb); port->write_urb = NULL; } else skw_usb_err(" memory leak port.w%d!!!!!!!!\n", infno); if(port->read_buffer) kfree(port->read_buffer); if(port->write_buffer) kfree(port->write_buffer); spin_lock_irqsave(&port->rx_urb_lock, flags); while(!list_empty(&port->rx_done_urb_list)) { urb = list_first_entry(&port->rx_done_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); if(urb->transfer_buffer) kfree(urb->transfer_buffer); usb_free_urb(urb); } spin_unlock_irqrestore(&port->rx_urb_lock, flags); usb_ports[infno] = NULL; usb_set_intfdata(interface, NULL); usb_put_dev(port->udev); usb_put_intf(interface); kfree(port); if (chip_en_gpio >= 0 && MODEM_DOWNLOAD_FAILED == modem_status) { modem_status = MODEM_HALT; msleep(50); gpio_set_value(chip_en_gpio, 1); skw_usb_info("retry to boot device\n"); } } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int skw_usb_io_pre_reset(struct usb_interface *interface) { /* there is a lock to prevent we reset a interface when * urb submit */ struct usb_port_struct *port; port = usb_get_intfdata(interface); return 0; } /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static int skw_usb_io_post_reset(struct usb_interface *interface) { struct usb_port_struct *port; port = usb_get_intfdata(interface); return 0; } #ifdef CONFIG_PM static int skw_usb_io_suspend(struct usb_interface *interface, pm_message_t message) { struct usb_port_struct *port; struct recovery_data *recovery = SKW_USB_GET_RECOVERY_DATA(); port = usb_get_intfdata(interface); if(usb_ports[1] == port) { int ret; u16 *count = (u16 *)port->read_buffer; modem_notify_event(DEVICE_SUSPEND_EVENT); ret = usb_control_msg(port->udev, usb_rcvctrlpipe(port->udev, 0), VENDOR_MSG_MODEM_SUSP, USB_DIR_IN| USB_TYPE_VENDOR|USB_RECIP_DEVICE, 1, 0, port->read_buffer, 2, 10); skw_usb_info("RET = %d packet suspended = %d\n", ret, *count); if (*count) msleep(10); } if (port->tx_urb_count) usb_kill_anchored_urbs(&port->write_submitted); port->suspend = 1; if(port->portno == 1 || port->read_urb->context) usb_kill_anchored_urbs(&port->read_submitted); if(port->write_urb->context) usb_kill_anchored_urbs(&port->write_submitted); if (port->portno==0 && recovery->cp_state) { recovery->cp_state = 0; cancel_delayed_work_sync(&skw_except_work); } #if KERNEL_VERSION(3, 2, 0) <= LINUX_VERSION_CODE skw_usb_info("port%d %s MSG\n", port->portno, PMSG_IS_AUTO(message)? "Auto":"None-auto"); #else skw_usb_info("port%d MSG not supported print!!\n", port->portno); #endif return 0; } static int skw_usb_io_resume(struct usb_interface *interface) { int retval = -1; struct usb_port_struct *port; struct urb *urb; port = usb_get_intfdata(interface); skw_usb_info("port%d enter...\n", port->portno); while(!list_empty(&port->suspend_urb_list)) { urb = list_first_entry(&port->suspend_urb_list, struct urb, urb_list); list_del_init(&urb->urb_list); if(port->portno == wifi_pdata.data_port) urb->context = port; usb_anchor_urb(urb, &port->read_submitted); retval = usb_submit_urb(urb, GFP_KERNEL); if (retval < 0) { usb_unanchor_urb(urb); skw_usb_info("is error!!! %d\n", retval); return retval; } } if (usb_ports[1]==port && port->suspend) { port->suspend = 0; modem_notify_event(DEVICE_RESUME_EVENT); } port->suspend = 0; return 0; } static int skw_usb_io_reset_resume(struct usb_interface *interface) { struct usb_port_struct *port; skw_usb_info("enter...\n"); port = usb_get_intfdata(interface); if (port) port->suspend++; skw_usb_io_resume(interface); return 0; } #endif /************************************************************************ *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ struct usb_driver skw_usb_io_driver = { .name = "skw_usb_io", .probe = skw_usb_io_probe, .disconnect = skw_usb_io_disconnect, #ifdef CONFIG_PM .suspend = skw_usb_io_suspend, .resume = skw_usb_io_resume, .reset_resume = skw_usb_io_reset_resume, #endif .pre_reset = skw_usb_io_pre_reset, .post_reset = skw_usb_io_post_reset, .id_table = skw_usb_io_id_table, .supports_autosuspend = 1, }; /** * wcn_usb_io_init() - init wcn_usb_io's memory and register this driver. * @void: void. */ static int __init skw_usb_io_init(void) { usb_bus_num = 0xff; usb_port_num = 0xff; wifi_data_pdev = NULL; bluetooth_pdev = NULL; log_port = NULL; usb_boot_data = NULL; #ifndef CONFIG_SEEKWAVE_PLD_RELEASE cls_recovery_mode_en = 1; #else cls_recovery_mode_en = 0; #endif wifi_port_share = 0; usb_speed_switching = 0; memset(usb_ports, 0, sizeof(usb_ports)); init_completion(&download_done); init_completion(&loop_completion); skw_usb_debugfs_init(); skw_usb_log_level_init(); chip_en_gpio = -1; modem_status = MODEM_OFF; skw_chipid = wifi_pdata.chipid; mutex_init(&g_recovery_data.except_mutex); INIT_DELAYED_WORK(&skw_except_work, skw_usb_exception_work); INIT_WORK(&add_device_work, add_devices_work); INIT_WORK(&dump_memory_worker, dump_memory_work); INIT_WORK(&usb_control_worker, usb_control_work); dump_memory_buffer = NULL; dump_log_size=NULL; dump_buffer_size = 0; usb_register(&skw_usb_io_driver); return seekwave_boot_init(NULL);; } /************************************************************************ *Copyright(C) 2020-2021: Seekwave tech LTD China *Decription: *Author:jiayong.yang *Date:2021-05-27 *Modfiy: * ********************************************************************* */ static void __exit skw_usb_io_exit(void) { int ret; if (chip_en_gpio >=0) { skw_chip_set_power(0); msleep(50); } if (usb_ports[0] && usb_ports[0]->udev) { skw_usb_info("reset SKWUSB device"); skw_reset_bus_dev(); } if (usb_boot_data && usb_boot_data->pdev && wifi_data_pdev && wifi_data_pdev->dev.parent == &usb_boot_data->pdev->dev) { skw_usb_info("unregister WIFI device\n"); platform_device_unregister(wifi_data_pdev); wifi_data_pdev = NULL; ret = 0; } seekwave_boot_exit(); skw_usb_debugfs_deinit(); cancel_delayed_work_sync(&skw_except_work); cancel_work_sync(&add_device_work); cancel_work_sync(&dump_memory_worker); cancel_work_sync(&usb_control_worker); dump_memory_buffer = NULL; dump_log_size=NULL; mutex_destroy(&g_recovery_data.except_mutex); if(bluetooth_pdev) platform_device_put(bluetooth_pdev); usb_deregister(&skw_usb_io_driver); if(wifi_data_pdev) platform_device_put(wifi_data_pdev); } module_init(skw_usb_io_init) module_exit(skw_usb_io_exit) MODULE_LICENSE("GPL v2");