/* * Copyright (C) 2015 Spreadtrum Communications Inc. * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "bufring.h" #include "mdbg_type.h" #include "wcn_log.h" #define MDBG_RING_LOCK_INIT(ring) mutex_init(ring->plock) #define MDBG_RING_LOCK_UNINIT(ring) mutex_destroy(ring->plock) #define MDBG_RING_LOCK(ring) mutex_lock(ring->plock) #define MDBG_RING_UNLOCK(ring) mutex_unlock(ring->plock) #define _MDBG_RING_REMAIN(rp, wp, size) ((u_long)(wp) >= (u_long)(rp) ?\ ((size) - (u_long)(wp) + (u_long)(rp)) :\ ((u_long)(rp) - (u_long)(wp))) /* valid buf for write */ long int mdbg_ring_free_space(struct mdbg_ring_t *ring) { return (long int)_MDBG_RING_REMAIN(ring->rp, ring->wp, ring->size); } static char *mdbg_ring_start(struct mdbg_ring_t *ring) { return ring->pbuff; } static char *mdbg_ring_end(struct mdbg_ring_t *ring) { return ring->end; } bool mdbg_ring_over_loop(struct mdbg_ring_t *ring, u_long len, int rw) { if (rw == MDBG_RING_R) { if ((u_long)ring->rp + len > (u_long)mdbg_ring_end(ring)) { ring->p_order_flag = 0; return true; } return false; } if ((u_long)ring->wp + len > (u_long)mdbg_ring_end(ring)) { ring->p_order_flag = 1; return true; } return false; } struct mdbg_ring_t *mdbg_ring_alloc(long int size) { struct mdbg_ring_t *ring = NULL; do { if (size < MDBG_RING_MIN_SIZE) { MDBG_ERR("size error:%ld", size); break; } ring = kmalloc(sizeof(struct mdbg_ring_t), GFP_KERNEL); if (ring == NULL) { MDBG_ERR("Ring malloc Failed."); break; } ring->pbuff = kmalloc(size, GFP_KERNEL); if (ring->pbuff == NULL) { MDBG_ERR("Ring buff malloc Failed."); break; } ring->plock = kmalloc(MDBG_RING_LOCK_SIZE, GFP_KERNEL); if (ring->plock == NULL) { MDBG_ERR("Ring lock malloc Failed."); break; } MDBG_RING_LOCK_INIT(ring); memset(ring->pbuff, 0, size); ring->size = size; ring->rp = ring->pbuff; ring->wp = ring->pbuff; ring->end = (char *)(((u_long)ring->pbuff) + (ring->size - 1)); ring->p_order_flag = 0; return ring; } while (0); mdbg_ring_destroy(ring); return NULL; } void mdbg_ring_destroy(struct mdbg_ring_t *ring) { if (unlikely(ZERO_OR_NULL_PTR(ring))) return; MDBG_LOG("ring = %p", ring); MDBG_LOG("ring->pbuff = %p", ring->pbuff); if (ring != NULL) { if (ring->pbuff != NULL) { MDBG_LOG("to free ring->pbuff."); kfree(ring->pbuff); ring->pbuff = NULL; } if (ring->plock != NULL) { MDBG_LOG("to free ring->plock."); MDBG_RING_LOCK_UNINIT(ring); kfree(ring->plock); ring->plock = NULL; } MDBG_LOG("to free ring."); kfree(ring); ring = NULL; } } int mdbg_ring_read(struct mdbg_ring_t *ring, void *buf, int len) { int len1, len2 = 0; int cont_len = 0; int read_len = 0; char *pstart = NULL; char *pend = NULL; static unsigned int total_len; if ((buf == NULL) || (ring == NULL) || (len == 0)) { MDBG_ERR("Ring Read Failed,Param Error!,buf=%p,ring=%p,len=%d", buf, ring, len); return -MDBG_ERR_BAD_PARAM; } MDBG_RING_LOCK(ring); cont_len = mdbg_ring_readable_len(ring); read_len = cont_len >= len ? len : cont_len; pstart = mdbg_ring_start(ring); pend = mdbg_ring_end(ring); MDBG_LOG("read_len=%d", read_len); MDBG_LOG("buf=%p", buf); MDBG_LOG("pstart=%p", pstart); MDBG_LOG("pend=%p", pend); MDBG_LOG("ring->wp = %p", ring->wp); MDBG_LOG("ring->rp=%p\n", ring->rp); if ((read_len == 0) || (cont_len == 0)) { MDBG_LOG("read_len = 0 OR Ring Empty."); MDBG_RING_UNLOCK(ring); return 0; /*ring empty*/ } if (mdbg_ring_over_loop(ring, read_len, MDBG_RING_R)) { MDBG_LOG("Ring loopover."); len1 = pend - ring->rp + 1; len2 = read_len - len1; if ((uintptr_t)buf > TASK_SIZE) { memcpy(buf, ring->rp, len1); memcpy((buf + len1), pstart, len2); } else { if (copy_to_user((void __user *)buf, (void *)ring->rp, len1) | copy_to_user((void __user *)(buf + len1), (void *)pstart, len2)) { MDBG_ERR("copy to user error!\n"); MDBG_RING_UNLOCK(ring); return -EFAULT; } } ring->rp = (char *)((u_long)pstart + len2); } else{ /* RP < WP */ if (ring->p_order_flag == 0) { if (((ring->rp + read_len) > ring->wp) && (mdbg_dev->open_count != 0)) WCN_ERR("read overlay\n"); } if ((uintptr_t)buf > TASK_SIZE) memcpy(buf, ring->rp, read_len); else if (copy_to_user((void __user *)buf, (void *)ring->rp, read_len)) { MDBG_ERR("copy to user error!\n"); MDBG_RING_UNLOCK(ring); return -EFAULT; } ring->rp += read_len; } total_len += read_len; wcn_pr_daterate(4, 1, total_len, "%s totallen:%u curread:%d wp:%p rp:%p", __func__, total_len, read_len, ring->wp, ring->rp); MDBG_LOG("<-----[read end] read len =%d.\n", read_len); MDBG_RING_UNLOCK(ring); return read_len; } /* * read: Rp = Wp: empty * write: Wp+1=Rp: full */ int mdbg_ring_write(struct mdbg_ring_t *ring, void *buf, unsigned int len) { int len1, len2 = 0; char *pstart = NULL; char *pend = NULL; static unsigned int total_len; MDBG_LOG("-->Ring Write len = %d\n", len); if ((ring == NULL) || (buf == NULL) || (len == 0)) { MDBG_ERR( "Ring Write Failed,Param Error!,buf=%p,ring=%p,len=%d", buf, ring, len); return -MDBG_ERR_BAD_PARAM; } pstart = mdbg_ring_start(ring); pend = mdbg_ring_end(ring); MDBG_LOG("pstart = %p", pstart); MDBG_LOG("pend = %p", pend); MDBG_LOG("buf = %p", buf); MDBG_LOG("len = %d", len); MDBG_LOG("ring->wp = %p", ring->wp); MDBG_LOG("ring->rp=%p", ring->rp); /* * ring buf valid space < len,need to write (buf_space-1) * and then write len-(buf_space-1) */ if (((mdbg_ring_free_space(ring) - 1) < len) && (mdbg_dev->open_count != 0)) { WCN_ERR("log buf is full, Discard the package=%d\n", len); wake_up_log_wait(); return len; } /* ring buf valid space > len, you can write freely */ if (mdbg_ring_over_loop(ring, len, MDBG_RING_W)) { MDBG_LOG("Ring overloop."); len1 = pend - ring->wp + 1; len2 = (len - len1) % ring->size; if ((uintptr_t)buf > TASK_SIZE) { memcpy(ring->wp, buf, len1); memcpy(pstart, (buf + len1), len2); } else { if (copy_from_user((void *)ring->wp, (void __user *)buf, len1) | copy_from_user((void *)pstart, (void __user *)(buf + len1), len2)) { MDBG_ERR("%s copy from user error!\n", __func__); return -EFAULT; } } ring->wp = (char *)((u_long)pstart + len2); } else{ /* RP > WP */ if ((uintptr_t)buf > TASK_SIZE) memcpy(ring->wp, buf, len); else if (copy_from_user((void *)ring->wp, (void __user *)buf, len)) { MDBG_ERR("%s copy from user error!\n", __func__); return -EFAULT; } ring->wp += len; } total_len += len; wcn_pr_daterate(4, 1, total_len, "%s totallen:%u cur_read:%u wp:%p rp:%p", __func__, total_len, len, ring->wp, ring->rp); MDBG_LOG("<------end len = %d\n", len); return len; } /* @timeout unit is ms */ int mdbg_ring_write_timeout(struct mdbg_ring_t *ring, void *buf, unsigned int len, unsigned int timeout) { unsigned int cnt = timeout / 20; while (cnt > 0 && (mdbg_ring_free_space(ring) - 1) < len) { msleep(20); if (--cnt == 0) MDBG_ERR("ringbuf is full, tiemout:%u\n", timeout); } return mdbg_ring_write(ring, buf, len); } char *mdbg_ring_write_ext(struct mdbg_ring_t *ring, long int len) { char *wp = NULL; MDBG_LOG("ring=%p,ring->wp=%p,len=%ld.", ring, ring->wp, len); if ((ring == NULL) || (len == 0)) { MDBG_ERR("Ring Write Ext Failed,Param Error!"); return NULL; } if (mdbg_ring_over_loop(ring, len, MDBG_RING_R) || mdbg_ring_will_full(ring, len)) { MDBG_LOG( "Ring Write Ext Failed,Ring State Error!,overloop=%d,full=%d.", mdbg_ring_over_loop(ring, len, MDBG_RING_R), mdbg_ring_will_full(ring, len)); return NULL; } MDBG_RING_LOCK(ring); wp = ring->wp; ring->wp += len; MDBG_LOG("return wp=%p,ring->wp=%p.", wp, ring->wp); MDBG_RING_UNLOCK(ring); return wp; } bool mdbg_ring_will_full(struct mdbg_ring_t *ring, int len) { return (len > mdbg_ring_free_space(ring)); } /* remain data for read */ long int mdbg_ring_readable_len(struct mdbg_ring_t *ring) { return ring->size - mdbg_ring_free_space(ring); } void mdbg_ring_clear(struct mdbg_ring_t *ring) { ring->rp = ring->wp; } void mdbg_ring_reset(struct mdbg_ring_t *ring) { ring->wp = ring->pbuff; ring->rp = ring->wp; } void mdbg_ring_print(struct mdbg_ring_t *ring) { WCN_DEBUG("ring buf status:ring->rp=%p,ring->wp=%p.\n", ring->rp, ring->wp); }