#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kthread.h> //#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/netdevice.h> #include <linux/ip.h> #include <linux/in.h> #include <linux/delay.h> #include "netlog.h" #define MODULE_NAME "netlog" #define dbg(fmt, args...) //printk(fmt, ##args) struct netlog_t { struct task_struct *thread; struct socket *sock; struct sockaddr_in addr; struct sockaddr_in peer; char *log; unsigned int loglen; unsigned int pw; unsigned int pr; //atomic_t lock; struct timer_list tmr_send; // timeout send struct timer_list tmr_poll; // watchdog unsigned char sendbuf[1024]; unsigned char recvbuf[128]; rwlock_t send_lock; struct work_struct work; struct work_struct work_poll; char running; char enabled; char kicked; }; static struct netlog_t *netlog = NULL; static const unsigned char g_szAckStart[]={'A'^0xFF, 'C'^0xFF, 'K'^0xFF, ':'^0xFF, 'S'^0xFF, 'T'^0xFF, 'A'^0xFF, 'R'^0xFF, 'T'^0xFF }; static const unsigned char g_szAckStop[]={'A'^0xFF, 'C'^0xFF, 'K'^0xFF, ':'^0xFF, 'S'^0xFF, 'T'^0xFF,'O'^0xFF, 'P'^0xFF}; #define NETLOG_AVAIL_BUF(n) (((n)->pr + NETLOG_MAX_BUFSIZE - 1 - (n)->pw) % NETLOG_MAX_BUFSIZE) #define NETLOG_DATA_SIZE(n) (((n)->pw - (n)->pr + NETLOG_MAX_BUFSIZE) % NETLOG_MAX_BUFSIZE) #define NEXT_IDX(k) ((k+1)%NETLOG_MAX_BUFSIZE) /* function prototypes */ static int ksocket_receive(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len); static int ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len); static int netlog_send_now(void); static void netlog_ctl(char new_state); extern void console_input(const char *buf, unsigned int len); static int netlog_is_enabled(void) { return netlog && netlog->enabled; } static void netlog_poll_func(unsigned long data) { dbg("%s: timeout!\n",__FUNCTION__); if (netlog_is_enabled()) schedule_work(&netlog->work_poll); } static void netlog_send_func(unsigned long data) { u32 datalen, next; netlog->kicked = 0; datalen = NETLOG_DATA_SIZE(netlog); //while (NETLOG_DATA_SIZE(netlog)) if (datalen > 0) schedule_work(&netlog->work); if (unlikely(datalen > NETLOG_SEND_THRESHOLD)) next = msecs_to_jiffies(10); else next = NETLOG_PRINT_INTERVAL; mod_timer(&netlog->tmr_send, jiffies + next); } static void netlog_ctl(char new_state) { unsigned long flags; local_irq_save(flags); if (new_state == netlog->enabled) { local_irq_restore(flags); return; } netlog->enabled = new_state; netlog->pr = netlog->pw = 0; netlog->loglen = NETLOG_MAX_BUFSIZE; netlog->kicked = 0; //atomic_set(&netlog->lock, 0); if (new_state) { netlog->log = kmalloc(NETLOG_MAX_BUFSIZE, GFP_KERNEL); if (!netlog->log) { printk("netlog: cannot alloc memory\n"); netlog->enabled = 0; } } else { if (timer_pending(&netlog->tmr_poll)) del_timer_sync(&netlog->tmr_poll); if (timer_pending(&netlog->tmr_send)) del_timer_sync(&netlog->tmr_send); kfree(netlog->log); } if (netlog->enabled) { rwlock_init(&netlog->send_lock); /* start poll timer */ init_timer(&netlog->tmr_poll); netlog->tmr_poll.data = 0; netlog->tmr_poll.function = netlog_poll_func; init_timer(&netlog->tmr_send); netlog->tmr_send.data = 0; netlog->tmr_send.function = netlog_send_func; mod_timer(&netlog->tmr_poll, jiffies + NETLOG_WATCHDOGVAL); mod_timer(&netlog->tmr_send, jiffies + NETLOG_PRINT_INTERVAL); } local_irq_restore(flags); printk("netlog enable=%d\n",netlog->enabled); } static inline char bit_reverse_char(char c) { return (c ^ 0xFF); } void netlog_emit_char(char c) { unsigned long flags; if (!netlog_is_enabled()) return; if ((!netlog->kicked) && (NETLOG_SEND_THRESHOLD < NETLOG_DATA_SIZE(netlog))) { mod_timer(&netlog->tmr_send, jiffies + msecs_to_jiffies(10)); netlog->kicked = 1; } local_irq_save(flags); netlog->log[netlog->pw] = bit_reverse_char(c); if (NEXT_IDX(netlog->pw) == netlog->pr) netlog->pr = NEXT_IDX(netlog->pr); netlog->pw = NEXT_IDX(netlog->pw); local_irq_restore(flags); } /* void netlog_early_out(const char *s, unsigned n) { while (n-- && *s) { if (*s == '\n') netlog_emit_char((unsigned char)'\r'); netlog_emit_char((unsigned char)*s); s++; } } */ /* return number of bytes fetched from netlog buffer */ static inline int netlog_peek_buf(char *buf, unsigned buflen) { unsigned int bytes_to_read, i; //unsigned long flags; bytes_to_read = (buflen > NETLOG_DATA_SIZE(netlog)) ? NETLOG_DATA_SIZE(netlog) : buflen; //local_irq_save(flags); for (i=0; i<bytes_to_read; i++) { //buf[i] = netlog->log[netlog->pr]; //netlog->pr = NEXT_IDX(netlog->pr); buf[i] = netlog->log[ (netlog->pr + i) % NETLOG_MAX_BUFSIZE ]; } //local_irq_restore(flags); return bytes_to_read; } static int netlog_send_now(void) { unsigned int to_send; int len; read_lock(&netlog->send_lock); to_send = netlog_peek_buf(netlog->sendbuf, sizeof(netlog->sendbuf)); len = ksocket_send(netlog->sock, &netlog->peer, netlog->sendbuf, to_send); if(len > 0) { netlog->pr = (netlog->pr + len) % NETLOG_MAX_BUFSIZE; } read_unlock(&netlog->send_lock); return len; } static int parse_input_cmd(const char* buf, int len) { int ret; const char *bufp=buf; //dbg("netlog_parseInputCmd---\n"); if(!memcmp(bufp, "CMD:START", 9)) ret=NETLOG_START; else if(!memcmp(bufp, "CMD:STOP", 8)) ret=NETLOG_STOP; else if(!memcmp(bufp, "INPUT:", 6)) ret=NETLOG_INPUT; else if(!memcmp(bufp,"SIG:POL",7)) ret=NETLOG_POLLING; else ret=0; dbg("return value %d\n",ret); return ret; } static void bit_reverse(char *buf,unsigned long len) { char* tempbuf=buf; while(len-- >0) { *tempbuf=bit_reverse_char(*tempbuf); tempbuf++; }; } static void netlog_process(const char *buf, unsigned long len) { switch (parse_input_cmd(buf, len)) { case NETLOG_START: if (netlog_is_enabled()) break; printk("netlog_start\n"); ksocket_send(netlog->sock, &netlog->peer, (u8 *)g_szAckStart, sizeof(g_szAckStart)); netlog_ctl(1); break; case NETLOG_STOP: if (!netlog_is_enabled()) break; dbg("netlog_stop\n"); ksocket_send(netlog->sock, &netlog->peer, (u8 *)g_szAckStop, sizeof(g_szAckStop)); netlog_ctl(0); break; case NETLOG_INPUT: if (!netlog_is_enabled()) break; console_input(buf+6,len-6); break; case NETLOG_POLLING: if (!netlog_is_enabled()) break; if (timer_pending(&netlog->tmr_poll)) mod_timer(&netlog->tmr_poll, jiffies + NETLOG_WATCHDOGVAL); else printk("%s(%d):\n", __FUNCTION__,__LINE__); //dbg("netlog_poll\n"); break; default: dbg("netlog_bug:\n"); } } static void netlog_work_func(struct work_struct *work) { netlog_send_now(); } static void netlog_work_poll_func(struct work_struct *work) { netlog_ctl(0); } static void kthread_start(void) { int size, err; /* kernel thread initialization */ //lock_kernel(); netlog->running = 1; current->flags |= PF_NOFREEZE; /* daemonize (take care with signals, after daemonize() they are disabled) */ //daemonize(MODULE_NAME); allow_signal(SIGKILL); //unlock_kernel(); INIT_WORK(&netlog->work, netlog_work_func); INIT_WORK(&netlog->work_poll, netlog_work_poll_func); /* create a socket */ if ( (err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &netlog->sock)) < 0) { printk(KERN_INFO MODULE_NAME": Could not create a datagram socket, error = %d\n", -ENXIO); goto out; } memset(&netlog->addr, 0, sizeof(struct sockaddr)); netlog->addr.sin_family = AF_INET; netlog->addr.sin_addr.s_addr = htonl(INADDR_ANY); netlog->addr.sin_port = htons(NETLOG_PORT); if ( (err = netlog->sock->ops->bind(netlog->sock, (struct sockaddr *)&netlog->addr, sizeof(struct sockaddr) ) ) < 0) { printk(KERN_INFO MODULE_NAME": Could not bind or connect to socket, error = %d\n", -err); goto close_and_out; } printk(KERN_INFO MODULE_NAME": listening on port %d\n", NETLOG_PORT); /* main loop */ do { memset(netlog->recvbuf, 0, sizeof(netlog->recvbuf)); size = ksocket_receive(netlog->sock, &netlog->peer, netlog->recvbuf, sizeof(netlog->recvbuf)); if (signal_pending(current)) break; if (size < 0) printk(KERN_INFO MODULE_NAME": error getting datagram, sock_recvmsg error = %d\n", size); else if (size > 0) { bit_reverse(netlog->recvbuf, size); /* data processing */ netlog_process(netlog->recvbuf, size); } } while (!kthread_should_stop()); printk("netlog: terminating\n"); netlog_ctl(0); close_and_out: cancel_work_sync(&netlog->work); sock_release(netlog->sock); netlog->sock = NULL; out: netlog->thread = NULL; netlog->running = 0; } static int ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len) { struct msghdr msg; struct iovec iov; mm_segment_t oldfs; int size = 0; if (sock->sk==NULL) return 0; iov.iov_base = buf; iov.iov_len = len; msg.msg_flags = MSG_DONTWAIT; msg.msg_name = addr; msg.msg_namelen = sizeof(struct sockaddr_in); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; oldfs = get_fs(); set_fs(KERNEL_DS); size = sock_sendmsg(sock,&msg,len); set_fs(oldfs); return size; } static int ksocket_receive(struct socket* sock, struct sockaddr_in* addr, unsigned char* buf, int len) { struct msghdr msg; struct iovec iov; mm_segment_t oldfs; int size = 0; if (sock->sk==NULL) return 0; iov.iov_base = buf; iov.iov_len = len; msg.msg_flags = 0; msg.msg_name = addr; msg.msg_namelen = sizeof(struct sockaddr_in); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; oldfs = get_fs(); set_fs(KERNEL_DS); size = sock_recvmsg(sock,&msg,len,msg.msg_flags); set_fs(oldfs); return size; } static int __init netlog_init(void) { netlog = kmalloc(sizeof(struct netlog_t), GFP_KERNEL); memset(netlog, 0, sizeof(struct netlog_t)); /* start kernel thread */ netlog->thread = kthread_run((void *)kthread_start, NULL, MODULE_NAME); if (IS_ERR(netlog->thread)) { printk(KERN_INFO MODULE_NAME": unable to start kernel thread\n"); kfree(netlog); netlog = NULL; return -ENOMEM; } return 0; } static void __exit netlog_exit(void) { //int err; if (netlog->thread==NULL) printk(KERN_INFO MODULE_NAME": no kernel thread to kill\n"); else { //lock_kernel(); //err = kill_pid(find_get_pid(netlog->thread->pid), SIGKILL, 1); //unlock_kernel(); kthread_stop(netlog->thread); /* wait for kernel thread to die */ //if (err < 0) // printk(KERN_INFO MODULE_NAME": unknown error %d while trying to terminate kernel thread\n",-err); //else //{ // while (netlog->running == 1) // msleep(10); // printk(KERN_INFO MODULE_NAME": succesfully killed kernel thread!\n"); //} } /* free allocated resources before exit */ if (netlog->sock != NULL) { sock_release(netlog->sock); netlog->sock = NULL; } kfree(netlog); netlog = NULL; printk(KERN_INFO MODULE_NAME": module unloaded\n"); } /* init and cleanup functions */ module_init(netlog_init); module_exit(netlog_exit); /* module information */ MODULE_DESCRIPTION("Netlog for remote debugging"); MODULE_AUTHOR("Andrew Chang <yachang@realtek.com>");