/* * * Seekwave Bluetooth driver * * Copyright (C) 2023 Seekwave Tech Ltd. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #if 1 #include #include "skw_common.h" #else #include #include "skw_btlog.h" #endif #ifndef SKWBT_INFO #define SKWBT_INFO(format, ...) pr_info("[SKWBTLOG_INFO] "format, ##__VA_ARGS__) #endif #ifndef SKWBT_ERROR #define SKWBT_ERROR(format, ...) pr_err("[SKWBTLOG_ERROR] "format, ##__VA_ARGS__) #endif #if SKWBT_LOG_PORT_EN typedef struct { uint16_t data_len; uint16_t data_len1; uint8_t is_from_cp; uint8_t is_from_cp1; uint16_t reserved; uint8_t data[1];// } __packed skwbt_log_pkt_st; typedef struct { struct class *skwbt_class; int skwbt_major; uint8_t is_open; struct sk_buff_head skwbt_rx_q; atomic_t rx_recv; wait_queue_head_t poll_wait_queue; char skwbt_is_open; } skwbt_log_port_info_st; #define SKWBT_LOGPORT_NAME "SKWBT_LOG" static void *skwbt_log_pdata = NULL; static skwbt_log_port_info_st *skwbt_log_port_info = NULL; char hex2char(int num) { char ch; if(num >= 0 && num <= 9) { ch = num + 48; } else if(num > 9 && num <= 15) { ch = (num - 10) + 65; } else { ch = '\0'; } return ch; } void hex2String(unsigned char hex[], unsigned char str[], int N) { int i = 0, j; for(i = 0, j = 0; i < N; i++, j += 2) { str[j] = hex2char((hex[i] & 0xF0) >> 4); str[j + 1] = hex2char(hex[i] & 0x0F); } str[N << 1] = 0; } void skwbt_log_open(char is_open) { if(skwbt_log_port_info && skwbt_log_pdata) { struct sv6160_platform_data *pdata = (struct sv6160_platform_data *)skwbt_log_pdata; if(pdata->bluetooth_log_disable) { pdata->bluetooth_log_disable(is_open > 0 ? 0 : 1); } SKWBT_INFO("skwbt_log_open, is_open:%d", is_open); } } void skwbt_log_port_set_bt_open(char is_open) { if(skwbt_log_port_info != NULL) { skwbt_log_port_info->skwbt_is_open = is_open; if(skwbt_log_port_info->is_open == 1) { skwbt_log_open(is_open); } } } void skwbt_log_port_data_write(uint8_t is_from_cp, uint8_t *data, uint16_t data_len) { if((skwbt_log_port_info != NULL) && (skwbt_log_port_info->is_open == 1) && (data_len >= 4))//min len is 4 { struct sk_buff *skb; skwbt_log_pkt_st *log_pkt; uint16_t total_len = data_len + 8; if(skb_queue_len(&skwbt_log_port_info->skwbt_rx_q) > 40)//40 packets { return ; } skb = alloc_skb(total_len, GFP_ATOMIC); if (!skb) { SKWBT_ERROR("skwbt log alloc skb failed, data_len: %d", (int)data_len); return ; } log_pkt = (skwbt_log_pkt_st *)skb_put(skb, total_len); log_pkt->data_len = data_len; log_pkt->data_len1 = data_len; log_pkt->is_from_cp = is_from_cp; log_pkt->is_from_cp1 = is_from_cp; log_pkt->reserved = 0x00; memcpy(log_pkt->data, data, data_len); #if 0 { uint8_t str_buffer[130] = {0x00}; hex2String(data, str_buffer, (data_len > 64) ? 64 : data_len); SKWBT_INFO("HCI log, is_from_cp:%d, len:%d, %s", is_from_cp, (int)data_len, str_buffer); } #endif //SKWBT_INFO("%s,is_from_cp:%d data_len: %d\n", __func__, is_from_cp, (int)data_len); skb_queue_tail(&skwbt_log_port_info->skwbt_rx_q, skb); atomic_inc(&skwbt_log_port_info->rx_recv); wake_up(&skwbt_log_port_info->poll_wait_queue); } } void skwbt_log_port_release_queue(void) { struct sk_buff *skb; if(skwbt_log_port_info != NULL) { while((skb = skb_dequeue(&skwbt_log_port_info->skwbt_rx_q))) { kfree_skb(skb); } } } static int skwbt_log_port_open(struct inode *inode, struct file *filp) { if((skwbt_log_port_info == NULL) || (skwbt_log_port_info->is_open != 0)) { return -EIO; } atomic_set(&skwbt_log_port_info->rx_recv, 0); skwbt_log_port_info->is_open = 1; SKWBT_INFO("%s, skwbt_is_open:%d", __func__, skwbt_log_port_info->skwbt_is_open); if(skwbt_log_port_info->skwbt_is_open) { skwbt_log_open(1); } return 0; } int skwbt_log_port_data_dequeue(int read_len, char __user *buf) { if(skwbt_log_port_info) { struct sk_buff *skb = skb_peek(&skwbt_log_port_info->skwbt_rx_q); if(skb) { int ret = 0; int data_size = 0; if(read_len >= skb->len) { data_size = skb->len; skb = skb_dequeue(&skwbt_log_port_info->skwbt_rx_q); ret = copy_to_user(buf, skb->data, data_size); //SKWBT_INFO("%s, len:%d, type:%d", __func__, data_size, skb->data[0]); kfree_skb(skb); } else//read_len < skb->len { data_size = read_len; ret = copy_to_user(buf, skb->data, read_len); skb_pull(skb, read_len); //SKWBT_INFO("%s, len:%d, type:%d, last:%d", __func__, data_size, skb->data[0], skb->len); } //SKWBT_INFO("%s enter..., read len:%d, data_size:%d\n", __func__, (int)read_len, data_size); if(ret == 0) { return data_size; } return -EFAULT; } } return 0; } static ssize_t skwbt_log_port_read(struct file *filp, char __user *buf, size_t read_len, loff_t *offt) { if((skwbt_log_port_info != NULL) && (skwbt_log_port_info->is_open == 1)) { if(!skb_queue_empty(&skwbt_log_port_info->skwbt_rx_q))//not empty { return skwbt_log_port_data_dequeue(read_len, buf); } else if(filp->f_flags & O_NONBLOCK) { return 0; } atomic_set(&skwbt_log_port_info->rx_recv, 0); wait_event_interruptible(skwbt_log_port_info->poll_wait_queue, atomic_read(&skwbt_log_port_info->rx_recv)); //SKWBT_INFO("%s exit...\n", __func__); return skwbt_log_port_data_dequeue(read_len, buf); } return 0; } static ssize_t skwbt_log_port_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { SKWBT_INFO("%s, len:%d", __func__, (int)cnt); return 0; } static int skwbt_log_port_flush(struct file *filp, fl_owner_t id) { skwbt_log_port_release_queue(); return 0; } static int skwbt_log_port_release(struct inode *inode, struct file *filp) { skwbt_log_port_info->is_open = 0; atomic_inc(&skwbt_log_port_info->rx_recv); wake_up(&skwbt_log_port_info->poll_wait_queue); skwbt_log_port_release_queue(); skwbt_log_open(0); return 0; } unsigned int skwbt_log_port_poll(struct file *filp, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(filp, &skwbt_log_port_info->poll_wait_queue, wait); if(!skb_queue_empty(&skwbt_log_port_info->skwbt_rx_q)) { mask = POLLIN | POLLRDNORM; } //SKWBT_INFO("%s enter..., data size:%d\n", __func__, queue_get_data_size()); return mask | POLLOUT | POLLWRBAND; } long skwbt_log_port_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { return 0; } static struct file_operations skwbt_log_port_fops = { .owner = THIS_MODULE, .open = skwbt_log_port_open, .read = skwbt_log_port_read, .write = skwbt_log_port_write, .flush = skwbt_log_port_flush, .poll = skwbt_log_port_poll, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) .unlocked_ioctl = skwbt_log_port_ioctl, #else .ioctl = skwbt_log_port_ioctl, #endif .release = skwbt_log_port_release, }; void skwbt_log_port_init_fail(void) { kfree(skwbt_log_port_info); skwbt_log_port_info = NULL; } void skwbt_log_port_set_pdata(void *pdata) { skwbt_log_pdata = pdata; SKWBT_INFO("%s, pdata:%p, skwbt_log_port_info:%p", __func__, pdata, skwbt_log_port_info); } void skwbt_log_port_init(void) { SKWBT_INFO("Seekwave Bluetooth Log init"); if(skwbt_log_port_info == NULL) { struct device *skw_device; int ret = 0; int val_size = sizeof(skwbt_log_port_info_st); skwbt_log_port_info = kmalloc(val_size, GFP_KERNEL); if(skwbt_log_port_info == NULL) { return ; } memset(skwbt_log_port_info, 0, val_size); skwbt_log_port_info->is_open = 0; skwbt_log_port_info->skwbt_class = class_create(THIS_MODULE, "skwbtlog_port"); if(IS_ERR(skwbt_log_port_info->skwbt_class)) { //int ret = PTR_ERR(skwbt_proc_log_info->skwbt_class); skwbt_log_port_info->skwbt_class = NULL; skwbt_log_port_init_fail(); return ; } ret = register_chrdev(0, SKWBT_LOGPORT_NAME, &skwbt_log_port_fops); if(ret < 0) { SKWBT_INFO("%s register_chrdev fail %d\n", __func__, ret); class_destroy(skwbt_log_port_info->skwbt_class); skwbt_log_port_info->skwbt_class = NULL; skwbt_log_port_init_fail(); return ; } skw_device = device_create(skwbt_log_port_info->skwbt_class, NULL, MKDEV(ret, 1), NULL, "%s", SKWBT_LOGPORT_NAME); if (!skw_device) { SKWBT_ERROR("%s, create device failed\n", __func__); unregister_chrdev(ret, SKWBT_LOGPORT_NAME); class_destroy(skwbt_log_port_info->skwbt_class); skwbt_log_port_init_fail(); return ; } skwbt_log_port_info->skwbt_major = ret; skb_queue_head_init(&skwbt_log_port_info->skwbt_rx_q); init_waitqueue_head(&skwbt_log_port_info->poll_wait_queue); } } void skwbt_log_port_exit(void) { SKWBT_INFO("Seekwave Bluetooth Proc Log exit\n"); if(skwbt_log_port_info != NULL) { skwbt_log_port_release_queue(); unregister_chrdev(skwbt_log_port_info->skwbt_major, SKWBT_LOGPORT_NAME); device_destroy(skwbt_log_port_info->skwbt_class, MKDEV(skwbt_log_port_info->skwbt_major, 1)); class_destroy(skwbt_log_port_info->skwbt_class); skwbt_log_port_info->skwbt_class = NULL; kfree(skwbt_log_port_info); skwbt_log_port_info = NULL; skwbt_log_pdata = NULL; } } EXPORT_SYMBOL_GPL(skwbt_log_port_data_write); EXPORT_SYMBOL_GPL(skwbt_log_port_init); EXPORT_SYMBOL_GPL(skwbt_log_port_exit); EXPORT_SYMBOL_GPL(skwbt_log_port_set_bt_open); EXPORT_SYMBOL_GPL(skwbt_log_port_set_pdata); #endif