/* tuxctl-ld.c
* The line discipline for the MP2 Tuxcontroller wrapper.
* Mark Murphy 2006
* Andrew Ofisher 2007
* Puskar Naha 2013
*/
#include #include #include #define uhoh(str, …) printk(KERN_EMERG “%s ” str, __FUNCTION__, ##__VA_ARGS__) /* Here’s an interesting tidbit: tty_struct has no synchronization available /* Line Discipline specific stuff */ #define sanity(data) if((data)->magic != TUXCTL_MAGIC){\ static int tuxctl_ldisc_open(struct tty_struct*); #define TUXCTL_BUFSIZE 64 char rx_buf[TUXCTL_BUFSIZE]; char tx_buf[TUXCTL_BUFSIZE]; } tuxctl_ldisc_data_t; static struct tty_ldisc tuxctl_ldisc = { int __init void __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->tx_start = 0; spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); return 0; static void spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); kfree(data); /* tuxctl_ldisc_rcv_buf spin_lock_irqsave(&tuxctl_ldisc_lock, flags); while(count– > 0 && !buf_full(data->rx_start, data->rx_end)) { spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); if(call){ /* tuxctl_ldisc_write_wakeup() /* I hope that this doesn’t need synchronization. */ spin_lock_irqsave(&tuxctl_ldisc_lock, flags); while(n <= room && !buf_empty(data->tx_start, data->tx_end)){ spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); sent = tty->driver->write(tty, buf, n); if(sent != n){ /*********** Interface to the char driver ********************/ /* tuxctl_ldisc_get() spin_lock_irqsave(&tuxctl_ldisc_lock, flags); return r; /* tuxctl_ldisc_put() spin_lock_irqsave(&tuxctl_ldisc_lock, flags); data = tty->disc_data; while (n > 0 && !buf_full(data->tx_start, data->tx_end)) { spin_unlock_irqrestore(&tuxctl_ldisc_lock, flags); /* Potential race conditions here … ?*/ tuxctl_ldisc_write_wakeup(tty); return n; /* tuxctl_ldisc_data_callback() unsigned char packet[12], *p; 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];
}
#define debug(str, …) printk(KERN_DEBUG “%s ” str, __FUNCTION__,\
## __VA_ARGS__)
* 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;
#define TUXCTL_MAGIC 0x74757863
uhoh(“MAGIC MISMATCH!\n”);\
BUG();}
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);
typedef struct tuxctl_ldisc_data {
unsigned long magic;
int rx_start, rx_end;
int tx_start, tx_end;
.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,
};
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);
tuxctl_ldisc_exit(void)
{
tty_unregister_ldisc(N_MOUSE);
printk(“tuxctl line discipline removed\n”);
}
module_exit(tuxctl_ldisc_exit);
data->rx_end = 0;
data->tx_end = 0;
tty->disc_data = data;
}
tuxctl_ldisc_close(struct tty_struct *tty)
{
tuxctl_ldisc_data_t *data;
unsigned long flags;
tty->disc_data = 0;
}
* 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;
if(0 != (data = tty->disc_data)){
call = 1;
data->rx_buf[data->rx_end] = *cp++;
buf_incidx(data->rx_end);
c++;
}
}
tuxctl_ldisc_data_callback(tty);
}
}
* 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;
room = tty->driver->write_room(tty);
data = tty->disc_data;
buf[n++] = data->tx_buf[data->tx_start];
buf_incidx(data->tx_start);
}
debug(“driver lied to us? We lost some data”);
}
}
* 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;
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);
}
* 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;
data->tx_buf[data->tx_end] = *buf++;
buf_incidx(data->tx_end);
–n;
}
}
* 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;
int n, i = 0, j;