/* stp_uart_tty_ioctl() * * Process IOCTL system call for the tty device. * * Arguments: * * tty pointer to tty instance data * file pointer to open file object for device * cmd IOCTL command code * arg argument for IOCTL call (cmd dependent) * * Return Value: Command dependent */ static int stp_uart_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { int err = 0; UART_DBG_FUNC("%s =>\n", __FUNCTION__); switch (cmd) { case HCIUARTSETPROTO: UART_DBG_FUNC("<!!> Set low_latency to TRUE <!!>\n"); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)) tty->port->low_latency = 1; #else tty->low_latency = 1; #endif break; default: UART_DBG_FUNC("<!!> n_tty_ioctl_helper <!!>\n"); err = n_tty_ioctl_helper(tty, file, cmd, arg); break; }; UART_DBG_FUNC("%s <=\n", __FUNCTION__); return err; }
static int stp_uart_tty_tx_write ( struct tty_struct *tty, const UINT8 *data, const UINT32 size ) { int written; int wr_count; int retry_left; int retry_delay_ms; wr_count = tty->ops->write(tty, data, size); if (likely(wr_count == size)) { /* perfect case! */ return wr_count; } UART_DBG_FUNC("tty write FAIL#1,size(%d)wr(%d)pid[%d/%s]\n", size, written, current->pid, current->comm); /* error handling */ retry_left = tx_retry_limit; retry_delay_ms = tx_retry_delay_ms; while ( (retry_left--) && (wr_count < size)) { /* do msleep if and only if STP-CORE using process context (caller's or * any other task) instead of any irq context (hardirq, softirq, tasklet * , timer, etc). */ msleep(retry_delay_ms); // TODO: to be refined by considering wr_count, current baud rate, etc. retry_delay_ms *= 2; written = tty->ops->write(tty, data + wr_count, size - wr_count); wr_count += written; } if (likely(wr_count == size)) { UART_DBG_FUNC("recovered,size(%d)retry_left(%d)delay(%d)\n", size, retry_left, retry_delay_ms); /* workable case! */ return wr_count; } /* return -written_count as error code and let caller to further error handle */ UART_ERR_FUNC("tty write FAIL#2,size(%d)wr(%d)retry_left(%d)pid[%d/%s]\n", size, wr_count, retry_left, current->pid, current->comm); return -wr_count; }
/* stp_uart_tty_close() * * Called when the line discipline is changed to something * else, the tty is closed, or the tty detects a hangup. */ static void stp_uart_tty_close(struct tty_struct *tty) { UART_DBG_FUNC("stp_uart_tty_close(): tty %p\n", tty); mtk_wcn_stp_register_if_tx(STP_UART_IF_TX, NULL); return; }
/* stp_uart_tty_open * * Called when line discipline changed to HCI_UART. * * Arguments: * tty pointer to tty info structure * Return Value: * 0 if success, otherwise error code */ static int stp_uart_tty_open(struct tty_struct *tty) { UART_DBG_FUNC("stp_uart_tty_opentty: %p\n", tty); tty->receive_room = 65536; tty->low_latency = 1; /* Flush any pending characters in the driver and line discipline. */ /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) /* definition changed!! */ if (tty->ldisc->ops->flush_buffer) { tty->ldisc->ops->flush_buffer(tty); } #else if (tty->ldisc.ops->flush_buffer) { tty->ldisc.ops->flush_buffer(tty); } #endif tty_driver_flush_buffer(tty); // init_MUTEX(&buf_mtx); //// spin_lock_init(&buf_lock); rd_idx = wr_idx = 0; stp_tty = tty; mtk_wcn_stp_register_if_tx(STP_UART_IF_TX, mtk_wcn_uart_tx); return 0; }
/* stp_uart_tty_ioctl() * * Process IOCTL system call for the tty device. * * Arguments: * * tty pointer to tty instance data * file pointer to open file object for device * cmd IOCTL command code * arg argument for IOCTL call (cmd dependent) * * Return Value: Command dependent */ static int stp_uart_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { int err = 0; UART_DBG_FUNC("%s =>\n", __FUNCTION__); switch (cmd) { case HCIUARTSETPROTO: UART_DBG_FUNC("<!!> Set low_latency to TRUE <!!>\n"); tty->low_latency = 1; break; default: UART_DBG_FUNC("<!!> n_tty_ioctl_helper <!!>\n"); err = n_tty_ioctl_helper(tty, file, cmd, arg); break; }; UART_DBG_FUNC("%s <=\n", __FUNCTION__); return err; }
/* stp_uart_tty_open * * Called when line discipline changed to HCI_UART. * * Arguments: * tty pointer to tty info structure * Return Value: * 0 if success, otherwise error code */ static int stp_uart_tty_open(struct tty_struct *tty) { UART_DBG_FUNC("original receive_room(%d) low_latency(%d) in tty(%p)\n", tty->receive_room, tty->low_latency, tty); tty->receive_room = 65536; #if LDISC_LOW_LATENCY tty->low_latency = 1; #endif UART_DBG_FUNC("set receive_room(%d) low_latency(%d) to tty(%p)\n", tty->receive_room, tty->low_latency, tty); /* Flush any pending characters in the driver and line discipline. */ /* FIXME: why is this needed. Note don't use ldisc_ref here as the open path is before the ldisc is referencable */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) /* definition changed!! */ if (tty->ldisc->ops->flush_buffer) { tty->ldisc->ops->flush_buffer(tty); } #else if (tty->ldisc.ops->flush_buffer) { tty->ldisc.ops->flush_buffer(tty); } #endif tty_driver_flush_buffer(tty); stp_uart_fifo_reset(); stp_uart_tty_tx_init(); stp_tty = tty; /* Register to STP-CORE */ mtk_wcn_stp_register_if_tx(STP_UART_IF_TX, mtk_wcn_uart_tx); return 0; }
static void stp_uart_rx_worker (struct work_struct *work) { unsigned int read; if (unlikely(!g_stp_uart_rx_fifo)) { UART_ERR_FUNC("NULL rx fifo!\n"); return; } if (unlikely(!g_stp_uart_rx_buf)) { UART_ERR_FUNC("NULL rx buf!\n"); return; } /* run until fifo becomes empty */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) while (kfifo_len(g_stp_uart_rx_fifo)) { read = kfifo_get(g_stp_uart_rx_fifo, g_stp_uart_rx_buf, LDISC_RX_BUF_SIZE); //UART_LOUD_FUNC("kfifo_get(%d)\n", read); if (likely(read)) { mtk_wcn_stp_parser_data((UINT8 *)g_stp_uart_rx_buf, read); } } #else while (!kfifo_is_empty(g_stp_uart_rx_fifo)) { read = kfifo_out(g_stp_uart_rx_fifo, g_stp_uart_rx_buf, LDISC_RX_BUF_SIZE); UART_DBG_FUNC("kfifo_out(%d)\n", read); //printk("rx_work:%d\n\r",read); if (likely(read)) { //UART_LOUD_FUNC("->%d\n", read); mtk_wcn_stp_parser_data((UINT8 *)g_stp_uart_rx_buf, read); //UART_LOUD_FUNC("<-\n", read); } } #endif return; }
static inline int stp_uart_tx_wakeup(struct tty_struct *tty) { int len = 0; int written = 0; int written_count = 0; static int i = 0; //unsigned long flags; // get data from ring buffer // down(&buf_mtx); UART_DBG_FUNC("++\n"); //// spin_lock_irqsave(&buf_lock, flags); #if 0 if((i > 1000) && (i % 5)== 0) { UART_INFO_FUNC("i=(%d), ****** drop data from uart******\n", i); i++; return 0; } else { UART_INFO_FUNC("i=(%d)at stp uart **\n", i); } #endif len = (wr_idx >= rd_idx) ? (wr_idx - rd_idx) : (MTKSTP_BUFFER_SIZE - rd_idx); if(len > 0 && len < MAX_PACKET_ALLOWED) { i++; /* * ops->write is called by the kernel to write a series of * characters to the tty device. The characters may come from * user space or kernel space. This routine will return the * number of characters actually accepted for writing. */ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); written = tty->ops->write(tty, &tx_buf[rd_idx], len); if(written != len) { UART_ERR_FUNC("Error(i-%d):[pid(%d)(%s)]tty-ops->write FAIL!len(%d)wr(%d)wr_i(%d)rd_i(%d)\n\r", i, current->pid, current->comm, len, written, wr_idx, rd_idx); return -1; } written_count = written; //printk("len = %d, written = %d\n", len, written); rd_idx = ((rd_idx + written) % MTKSTP_BUFFER_SIZE); // all data is accepted by UART driver, check again in case roll over len = (wr_idx >= rd_idx) ? (wr_idx - rd_idx) : (MTKSTP_BUFFER_SIZE - rd_idx); if(len > 0 && len < MAX_PACKET_ALLOWED) { set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); written = tty->ops->write(tty, &tx_buf[rd_idx], len); if (written != len) { UART_ERR_FUNC("Error(i-%d):[pid(%d)(%s)]len(%d)wr(%d)wr_i(%d)rd_i(%d)\n\r", i, current->pid, current->comm, len, written, wr_idx, rd_idx); return -1; } rd_idx = ((rd_idx + written) % MTKSTP_BUFFER_SIZE); written_count += written; } else if(len < 0 || len >= MAX_PACKET_ALLOWED) { UART_ERR_FUNC("Warnning(i-%d):[pid(%d)(%s)]length verfication(external) warnning,len(%d), wr_idx(%d), rd_idx(%d)!\n\r", i, current->pid, current->comm, len, wr_idx, rd_idx); return -1; } } else { UART_ERR_FUNC("Warnning(i-%d):[pid(%d)(%s)]length verfication(external) warnning,len(%d), wr_idx(%d), rd_idx(%d)!\n\r", i, current->pid, current->comm, len, wr_idx, rd_idx); return -1; } //up(&buf_mtx); //// spin_unlock_irqrestore(&buf_lock, flags); UART_DBG_FUNC("--\n"); return written_count; }
INT32 mtk_wcn_uart_tx(const UINT8 *data, const UINT32 size, UINT32 *written_size) { int room; //int idx = 0; //unsigned long flags; unsigned int len; //static int tmp=0; static int i = 0; if(stp_tty == NULL) return -1; UART_DBG_FUNC("++\n"); (*written_size) = 0; // put data into ring buffer //down(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //spin_lock_irqsave(&buf_lock, flags); room = (wr_idx >= rd_idx) ? (MTKSTP_BUFFER_SIZE - (wr_idx - rd_idx) - 1) : (rd_idx - wr_idx - 1); UART_DBG_FUNC("r(%d)s(%d)wr_i(%d)rd_i(%d)\n\r", room, size, wr_idx, rd_idx); /* [PatchNeed] Block copy instead of byte copying */ if(data == NULL){ UART_ERR_FUNC("pid(%d)(%s): data is NULL\n", current->pid, current->comm); (*written_size) = 0; UART_DBG_FUNC("--\n"); return -2; } #if 1 if(unlikely(size > room)){ UART_ERR_FUNC("pid(%d)(%s)room is not available, size needed(%d), wr_idx(%d), rd_idx(%d), room left(%d)\n", current->pid, current->comm, size, wr_idx, rd_idx, room); UART_DBG_FUNC("--\n"); (*written_size) = 0; return -3; } else { /* wr_idx : the position next to write rd_idx : the position next to read */ len = min(size, MTKSTP_BUFFER_SIZE - (unsigned int)wr_idx); memcpy(&tx_buf[wr_idx], &data[0], len); memcpy(&tx_buf[0], &data[len], size - len); wr_idx = (wr_idx + size) % MTKSTP_BUFFER_SIZE; UART_DBG_FUNC("r(%d)s(%d)wr_i(%d)rd_i(%d)\n\r", room, size, wr_idx, rd_idx); i++; if (size < 0) { UART_ERR_FUNC("Error(i-%d):[pid(%d)(%s)]len(%d)size(%d)wr_i(%d)rd_i(%d)\n\r", i, current->pid, current->comm, len, size, wr_idx, rd_idx); (*written_size) = 0; } else if (size == 0) { (*written_size) = 0; } else if (size < MAX_PACKET_ALLOWED) { //only size ~(0, 2000) is allowed (*written_size) = stp_uart_tx_wakeup(stp_tty); if(*written_size < 0) { //reset read and write index of tx_buffer, is there any risk? wr_idx = rd_idx = 0; *written_size = 0; } } else { //we filter all packet with size > 2000 UART_ERR_FUNC("Warnning(i-%d):[pid(%d)(%s)]len(%d)size(%d)wr_i(%d)rd_i(%d)\n\r", i, current->pid, current->comm, len, size, wr_idx, rd_idx); (*written_size)= 0; } } #endif #if 0 while((room > 0) && (size > 0)) { tx_buf[wr_idx] = data[idx]; wr_idx = ((wr_idx + 1) % MTKSTP_BUFFER_SIZE); idx++; room--; size--; (*written_size)++; } #endif //up(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //// spin_lock_irqsave(&buf_lock, flags); /*[PatchNeed]To add a tasklet to shedule Uart Tx*/ UART_DBG_FUNC("--\n"); return 0; }
static int stp_uart_fifo_init(void) { int err = 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)) g_stp_uart_rx_buf = vzalloc(LDISC_RX_BUF_SIZE); if (!g_stp_uart_rx_buf) { UART_ERR_FUNC("kfifo_alloc failed (kernel version >= 2.6.37)\n"); err = -4; goto fifo_init_end; } #else g_stp_uart_rx_buf = vmalloc(LDISC_RX_BUF_SIZE); if (!g_stp_uart_rx_buf) { UART_ERR_FUNC("kfifo_alloc failed (kernel version < 2.6.37)\n"); err = -4; goto fifo_init_end; } memset(g_stp_uart_rx_buf, 0, LDISC_RX_BUF_SIZE); #endif UART_INFO_FUNC("g_stp_uart_rx_buf alloc ok(0x%p, %d)\n", g_stp_uart_rx_buf, LDISC_RX_BUF_SIZE); /*add rx fifo*/ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)) spin_lock_init(&g_stp_uart_rx_fifo_spinlock); g_stp_uart_rx_fifo = kfifo_alloc(LDISC_RX_FIFO_SIZE, GFP_KERNEL, &g_stp_uart_rx_fifo_spinlock); if (NULL == g_stp_uart_rx_fifo) { UART_ERR_FUNC("kfifo_alloc failed (kernel version < 2.6.33)\n"); err = -1; goto fifo_init_end; } #else /* allocate struct kfifo first */ g_stp_uart_rx_fifo = kzalloc(sizeof(struct kfifo), GFP_KERNEL); if (NULL == g_stp_uart_rx_fifo) { err = -2; UART_ERR_FUNC("kzalloc struct kfifo failed (kernel version > 2.6.33)\n"); goto fifo_init_end; } /* allocate kfifo data buffer then */ err = kfifo_alloc(g_stp_uart_rx_fifo, LDISC_RX_FIFO_SIZE, GFP_KERNEL); if (0 != err) { UART_ERR_FUNC("kfifo_alloc failed, err(%d)(kernel version > 2.6.33)\n", err); kfree(g_stp_uart_rx_fifo); g_stp_uart_rx_fifo = NULL; err = -3; goto fifo_init_end; } #endif UART_INFO_FUNC("g_stp_uart_rx_fifo alloc ok\n"); fifo_init_end: if (0 == err) { /* kfifo init ok */ kfifo_reset(g_stp_uart_rx_fifo); UART_DBG_FUNC("g_stp_uart_rx_fifo init success\n"); } else { UART_ERR_FUNC("stp_uart_fifo_init() fail(%d)\n", err); if (g_stp_uart_rx_buf) { UART_ERR_FUNC("free g_stp_uart_rx_buf\n"); vfree(g_stp_uart_rx_buf); g_stp_uart_rx_buf = NULL; } } return err; }
/* stp_uart_tty_open * * Arguments: * tty pointer to tty info structure * data pointer to data buffer to be written * size data buffer length to be written * Return Value: * > 0 if success, otherwise error code */ static int stp_uart_tty_tx_write ( struct tty_struct *tty, const UINT8 *data, const UINT32 size ) { int ret; int len; int written; int written_count; int room; unsigned old_wr; unsigned old_rd; //unsigned long flags; UART_LOUD_FUNC("++\n"); /* Phase-I: put data into STP-UART ring buffer "tx_buf" */ /* wr_idx : the position next to write * rd_idx : the position next to read */ //down(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //spin_lock_irqsave(&buf_lock, flags); old_wr = wr_idx; old_rd = rd_idx; /* check left room size */ room = (wr_idx >= rd_idx) ? (STP_UART_TX_BUF_SIZE - (wr_idx - rd_idx) - 1) : (rd_idx - wr_idx - 1); UART_DBG_FUNC("before data in:r(%d)s(%d)wr_i(%d)rd_i(%d)\n", room, size, wr_idx, rd_idx); if (unlikely(size > room)) { UART_ERR_FUNC("buf unavailable FAIL#1,size(%d),wr_idx(%d),rd_idx(%d),room(%d),pid[%d/%s]\n", size, wr_idx, rd_idx, room, current->pid, current->comm); //up(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //spin_unlock_irqrestore(&buf_lock, flags); return -1; } else { len = min(size, STP_UART_TX_BUF_SIZE - (unsigned int)wr_idx); memcpy(&tx_buf[wr_idx], &data[0], len); memcpy(&tx_buf[0], &data[len], size - len); wr_idx = (wr_idx + size) % STP_UART_TX_BUF_SIZE; UART_DBG_FUNC("after data in: r(%d)s(%d)wr_i(%d)rd_i(%d)\n", room, size, wr_idx, rd_idx); } //up(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //spin_unlock_irqrestore(&buf_lock, flags); /* Phase-II: get data from the buffer and send to tty UART. * May be seperated into another context. */ //down(&buf_mtx); /* [PatchNeed] spin_lock_irqsave is redundant */ //spin_lock_irqsave(&buf_lock, flags); written_count = 0; len = (wr_idx >= rd_idx) ? (wr_idx - rd_idx) : (STP_UART_TX_BUF_SIZE - rd_idx); if (likely(len > 0 && len < MAX_PACKET_ALLOWED)) { /* TTY_DO_WRITE_WAKEUP is used for "Call write_wakeup after queuing new" * but stp_uart_tty_wakeup() is empty and unused now! */ //set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); /* * ops->write is called by the kernel to write a series of * characters to the tty device. The characters may come from * user space or kernel space. This routine will return the * number of characters actually accepted for writing. */ written = tty->ops->write(tty, &tx_buf[rd_idx], len); if (written != len) { UART_ERR_FUNC("tty-ops->write FAIL#2,len(%d)wr(%d)wr_i(%d)rd_i(%d),pid[%d/%s]\n", len, written, wr_idx, rd_idx, current->pid, current->comm); ret = -2; goto tx_write_out_unlock_old; } written_count = written; rd_idx = ((rd_idx + written) % STP_UART_TX_BUF_SIZE); // all data is accepted by UART driver, check again in case roll over len = (wr_idx >= rd_idx) ? (wr_idx - rd_idx) : (STP_UART_TX_BUF_SIZE - rd_idx); if (len > 0 && len < MAX_PACKET_ALLOWED) { /* TTY_DO_WRITE_WAKEUP is used for "Call write_wakeup after queuing new" * but stp_uart_tty_wakeup() is empty and unused now! */ //set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); written = tty->ops->write(tty, &tx_buf[rd_idx], len); if (unlikely(written != len)) { UART_ERR_FUNC("tty-ops->write FAIL#3,len(%d)wr(%d)wr_i(%d)rd_i(%d),pid[%d/%s]\n", len, written, wr_idx, rd_idx, current->pid, current->comm); ret = -3; goto tx_write_out_unlock_old; } rd_idx = ((rd_idx + written) % STP_UART_TX_BUF_SIZE); written_count += written; } else if (unlikely(len < 0 || len >= MAX_PACKET_ALLOWED)) { UART_ERR_FUNC("FAIL#4,len(%d)wr_i(%d)rd_i(%d),pid[%d/%s]\n", len, wr_idx, rd_idx, current->pid, current->comm); ret = -4; goto tx_write_out_unlock_old; } } else { UART_ERR_FUNC("FAIL#5,len(%d)wr_i(%d)rd_i(%d),pid[%d/%s]\n", len, wr_idx, rd_idx, current->pid, current->comm); ret = -5; goto tx_write_out_unlock_old; } /* success case */ ret = written_count; tx_write_out_unlock_old: if (unlikely(ret < 0)) { //reset read and write index of tx_buffer, is there any risk? wr_idx = rd_idx = 0; UART_ERR_FUNC("err(%d)reset fifo idx\n", ret); } if (unlikely(wr_idx != rd_idx)) { UART_WARN_FUNC("--wr(%d)rd(%d)size(%d)old wr(%d)rd(%d)\n", wr_idx, rd_idx, size, old_wr, old_rd); } else { UART_LOUD_FUNC("--wr(%d) rd(%d)\n", wr_idx, rd_idx); } //up(&buf_mtx); //spin_unlock_irqrestore(&buf_lock, flags); return ret; }