https://powcoder.com 代写代考加微信 powcoder Assignment Project Exam Help Add WeChat powcoder /* tuxctl-ld.c * The line discipline for the MP2 Tuxcontroller wrapper. * Mark Murphy 2006 * Andrew Ofisher 2007 * Puskar Naha 2013 */ #include #include #include #include #include #include #include "tuxctl-ld.h" #define uhoh(str, ...) printk(KERN_EMERG "%s " str, __FUNCTION__, ##__VA_ARGS__) #define debug(str, ...) printk(KERN_DEBUG "%s " str, __FUNCTION__,\ ## __VA_ARGS__) /* Here's an interesting tidbit: tty_struct has no synchronization available * to us to protect against races involving the tty->disc_data field. So, * rather than do something tricky, we'll employ the 'one-big-freakin-lock' * strategy for synchronizing the methods in this file. This can't be a * semaphore because of the following chain of function calls: * * rs_interrupt() (serial.c) * tty_flip_buffer_push() (tty_io.c) * flush_to_ldisc() (tty_io.c) * ldisc.receive_buf() (this file) * * Specifically, rs_interrupt is, well, an interrupt. Sleeping in an * interrupt is a recipe for breaking things, so a spinlock it is. */ static spinlock_t tuxctl_ldisc_lock = SPIN_LOCK_UNLOCKED; /* Line Discipline specific stuff */ #define TUXCTL_MAGIC 0x74757863 #define sanity(data) if((data)->magic != TUXCTL_MAGIC){\ uhoh("MAGIC MISMATCH!\n");\ BUG();} static int tuxctl_ldisc_open(struct tty_struct*); static void tuxctl_ldisc_close(struct tty_struct*); static void tuxctl_ldisc_rcv_buf(struct tty_struct*, const unsigned char *, char *, int); static void tuxctl_ldisc_write_wakeup(struct tty_struct*); static void tuxctl_ldisc_data_callback(struct tty_struct *tty); #define TUXCTL_BUFSIZE 64 typedef struct tuxctl_ldisc_data { unsigned long magic; char rx_buf[TUXCTL_BUFSIZE]; int rx_start, rx_end; char tx_buf[TUXCTL_BUFSIZE]; int tx_start, tx_end; } tuxctl_ldisc_data_t; static struct tty_ldisc tuxctl_ldisc = { .magic = TUXCTL_MAGIC, .name = "tuxcontroller", .open = tuxctl_ldisc_open, .close = tuxctl_ldisc_close, .ioctl = tuxctl_ioctl, .receive_buf = tuxctl_ldisc_rcv_buf, .write_wakeup = tuxctl_ldisc_write_wakeup, }; int __init tuxctl_ldisc_init(void) { int err = 0; if((err = tty_register_ldisc(N_MOUSE, &tuxctl_ldisc))){ debug("tuxctl line discipline register failed\n"); }else{ printk("tuxctl line discipline registered\n"); } return err; } module_init(tuxctl_ldisc_init); void __exit tuxctl_ldisc_exit(void) { tty_unregister_ldisc(N_MOUSE); printk("tuxctl line discipline removed\n"); } module_exit(tuxctl_ldisc_exit); #define buf_used(start,end) ((start) <= (end) ? \ ((end) - (start)) \ : (TUXCTL_BUFSIZE + (end) - (start))) #define buf_room(start,end) (TUXCTL_BUFSIZE - buf_used(start,end) - 1) #define buf_empty(start, end) ((start) == (end)) #define buf_full(start, end) ((((end)+1)%TUXCTL_BUFSIZE) == (start)) #define buf_incidx(idx) ((idx) = ((idx)+1) % TUXCTL_BUFSIZE) static int tuxctl_ldisc_open(struct tty_struct *tty) { tuxctl_ldisc_data_t *data; unsigned long flags; if(!(data = kmalloc(sizeof(*data), GFP_KERNEL))){ uhoh("kmalloc failed!\n"); return -ENOMEM; } spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data->magic = TUXCTL_MAGIC; data->rx_start = 0; data->rx_end = 0; data->tx_start = 0; data->tx_end = 0; tty->disc_data = data; spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); return 0; } static void tuxctl_ldisc_close(struct tty_struct *tty) { tuxctl_ldisc_data_t *data; unsigned long flags; spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; tty->disc_data = 0; spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); kfree(data); } /* tuxctl_ldisc_rcv_buf * The receive_buf() method of our line discipline. It receives count bytes * from cp. fp points to some flag/error bytes which I conveniently ignore. * This is called when there are bytes received from the serial driver, and * is called from an interrupt handler. */ static void tuxctl_ldisc_rcv_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) { int call = 0, c=0; unsigned long flags; tuxctl_ldisc_data_t *data; spin_lock_irqsave(&tuxctl_ldisc_lock, flags); if(0 != (data = tty->disc_data)){ call = 1; while(count-- > 0 && !buf_full(data->rx_start, data->rx_end)) { data->rx_buf[data->rx_end] = *cp++; buf_incidx(data->rx_end); c++; } } spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); if(call){ tuxctl_ldisc_data_callback(tty); } } /* tuxctl_ldisc_write_wakeup() * Called by the lower level serial driver when it can accept more */ static void tuxctl_ldisc_write_wakeup(struct tty_struct *tty) { tuxctl_ldisc_data_t *data; int sent, n = 0, room; char buf[TUXCTL_BUFSIZE]; unsigned long flags; /* I hope that this doesn't need synchronization. */ room = tty->driver->write_room(tty); spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; while(n <= room && !buf_empty(data->tx_start, data->tx_end)){ buf[n++] = data->tx_buf[data->tx_start]; buf_incidx(data->tx_start); } spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); sent = tty->driver->write(tty, buf, n); if(sent != n){ debug("driver lied to us? We lost some data"); } } /*********** Interface to the char driver ********************/ /* tuxctl_ldisc_get() * Read bytes that the line-discipline has received from the controller. * Returns the number of bytes actually read, or -1 on error (if, for * example, the first argument is invalid. */ int tuxctl_ldisc_get(struct tty_struct *tty, char *buf, int n) { tuxctl_ldisc_data_t *data; unsigned long flags; int r = 0; spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; while(n-- > 0 && !buf_empty(data->rx_start, data->rx_end)){ *buf++ = data->rx_buf[data->rx_start]; buf_incidx(data->rx_start); r++; } spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); return r; } /* tuxctl_ldisc_put() * Write bytes out to the device. Returns the number of bytes *not* written. * This means, 0 on success and >0 if the line discipline's internal buffer * is full. */ int tuxctl_ldisc_put(struct tty_struct *tty, char const *buf, int n) { tuxctl_ldisc_data_t *data; unsigned long flags; spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; while (n > 0 && !buf_full(data->tx_start, data->tx_end)) { data->tx_buf[data->tx_end] = *buf++; buf_incidx(data->tx_end); --n; } spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); /* Potential race conditions here ... ?*/ tuxctl_ldisc_write_wakeup(tty); return n; } /* tuxctl_ldisc_data_callback() * This is the function called from the line-discipline when data is * available from the device. This is how responses to polling the buttons * and ACK's for setting the LEDs will be transmitted to the tuxctl driver. * The tuxctl driver must implement this function. The 'tty' parameter * can be passed back to the tuxctl_ldisc_read function to read * data from the lower-level buffers. * * IMPORTANT: This function is called from an interrupt context, so it * cannot acquire any semaphores or otherwise sleep, or access * the 'current' pointer. It also must not take up too much time. */ static void tuxctl_ldisc_data_callback(struct tty_struct *tty) { /* Should probably synchronize these */ static unsigned char saved[2]; static int n_saved = 0; unsigned char packet[12], *p; int n, i = 0, j; for(i=0; i< n_saved; i++) packet[i] = saved[i]; n = n_saved + tuxctl_ldisc_get(tty, packet + n_saved, 12 - n_saved); /* Look at all bytes as potential packet beginnings except for * the last two bytes. Save them for next time. */ for(i = 0; i < n-2; ){ p = packet + i; /* Check the framing bits to detect lost bytes */ if(!(p[0]&0x80) && p[1]&0x80 && p[2]&0x80){ tuxctl_handle_packet(tty, p); i += 3; }else{ i++; } } /* Save the leftovers - extra bytes for breakfast */ n_saved = n - i; for(j = 0; j < n_saved; j++) saved[j] = packet[i+j]; }