From: Ballard J. <sac...@ho...> - 2004-09-19 20:02:23
|
/*=0A= * Copyright (C) 2004 Dan Aloni <da...@co...>=0A= *=0A= * Cooperative Linux Serial Line implementation=0A= * =0A= * Compatible with UML, also based on some code from there.=0A= * Also based on The tiny_tty.c example driver by Greg Kroah-Hartman = (gr...@kr...).=0A= */=0A= =0A= /*=0A= * 20040908: Ballard, Jonathan H. <jhb...@ho...>=0A= * : Implemented cocd_task() & throttle=0A= */=0A= =0A= #include <linux/major.h>=0A= #include <linux/config.h>=0A= #include <linux/module.h>=0A= #include <linux/init.h>=0A= #include <linux/fs.h>=0A= #include <linux/errno.h>=0A= #include <linux/major.h>=0A= #include <linux/stat.h>=0A= #include <linux/file.h>=0A= #include <linux/ioctl.h>=0A= #include <linux/device.h>=0A= #include <linux/console.h>=0A= =0A= #include <linux/workqueue.h>=0A= #include <linux/devfs_fs_kernel.h>=0A= #include <linux/tty.h>=0A= #include <linux/tty_flip.h>=0A= =0A= #include <linux/cooperative_internal.h>=0A= =0A= #include <asm/uaccess.h>=0A= =0A= struct cocd_tty {=0A= struct tty_struct *tty; /* pointer to the tty for this device */=0A= struct semaphore sem; /* locks this structure */=0A= int open_count; /* number of times this port has been opened */=0A= int throttled;=0A= };=0A= =0A= static struct tty_driver *cocd_driver =3D NULL;=0A= static struct work_struct cocd_work;=0A= DECLARE_MUTEX(cocd_sem);=0A= =0A= =0A= =0A= int cocd_open(struct tty_struct *tty, struct file * filp)=0A= {=0A= struct cocd_tty *cocd =3D NULL;=0A= =0A= down(&cocd_sem);=0A= =0A= MOD_INC_USE_COUNT;=0A= =0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd) {=0A= cocd =3D kmalloc(sizeof(*cocd), GFP_KERNEL);=0A= if (!cocd) {=0A= MOD_DEC_USE_COUNT;=0A= =0A= up(&cocd_sem);=0A= return -ENOMEM;=0A= }=0A= =0A= init_MUTEX_LOCKED(&cocd->sem);=0A= cocd->open_count =3D 0;=0A= cocd->tty =3D tty;=0A= cocd->throttled =3D 0;=0A= tty->driver_data =3D cocd;=0A= tty->low_latency =3D 1;=0A= } else {=0A= down (&cocd->sem);=0A= }=0A= =0A= cocd->open_count++;=0A= =0A= up(&cocd->sem);=0A= up(&cocd_sem);=0A= =0A= return 0;=0A= }=0A= =0A= void cocd_close(struct tty_struct *tty, struct file * filp)=0A= {=0A= struct cocd_tty *cocd =3D NULL;=0A= =0A= down(&cocd_sem);=0A= =0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd) {=0A= printk("cocd: no attached struct\n");=0A= goto out;=0A= }=0A= =0A= down(&cocd->sem);=0A= if (cocd->open_count =3D=3D 1) { /* last close */=0A= tty->driver_data =3D NULL;=0A= }=0A= cocd->open_count--;=0A= up(&cocd->sem);=0A= =0A= if (tty->driver_data =3D=3D NULL)=0A= kfree(cocd);=0A= =0A= out:=0A= MOD_DEC_USE_COUNT;=0A= =0A= up(&cocd_sem);=0A= }=0A= =0A= static unsigned cocd_task_count;=0A= =0A= static void cocd_task(void *data)=0A= {=0A= static co_message_node_t *input;=0A= static unsigned input_index;=0A= struct cocd_tty *cocd;=0A= struct tty_struct *tty;=0A= co_linux_message_t *message;=0A= char *p, *e, *m;=0A= =0A= down(&cocd_sem);=0A= if(cocd_task_count++)=0A= printk("cocd_task collision\n");=0A= up(&cocd_sem);=0A= =0A= if(!input) {=0A= if(!co_get_message(&input, CO_DEVICE_SERIAL)) {=0A= input =3D 0;=0A= goto cocd_task_exit;=0A= }=0A= if(!input)=0A= goto cocd_task_exit;=0A= }=0A= do {=0A= message =3D (co_linux_message_t *)&input->msg.data;=0A= if (message->unit >=3D CO_MODULE_MAX_SERIAL)=0A= goto next_message;=0A= tty =3D cocd_driver->ttys[message->unit];=0A= if (!tty)=0A= goto next_message;=0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd)=0A= goto next_message;=0A= p =3D message->data + input_index;=0A= e =3D message->data + message->size;=0A= m =3D p;=0A= do {=0A= down(&cocd_sem);=0A= if(!tty->driver_data) { // detects a close=0A= cocd_task_count=3D0;=0A= up(&cocd_sem);=0A= return;=0A= }=0A= down(&cocd->sem);=0A= if(cocd->throttled) {=0A= up(&cocd->sem);=0A= input_index =3D (unsigned)(p - message->data);=0A= schedule_delayed_work(&cocd_work, 1);=0A= goto cocd_task_exit;=0A= }=0A= up(&cocd->sem);=0A= if(e < (m +=3D (TTY_FLIPBUF_SIZE - tty->flip.count)))=0A= m =3D e;=0A= while(p < m)=0A= tty_insert_flip_char(tty, *(p++), 0);=0A= if(tty->flip.count >=3D TTY_FLIPBUF_SIZE) {=0A= tty_flip_buffer_push(tty);=0A= }=0A= } while(p < e);=0A= if(tty->flip.count) {=0A= down(&cocd->sem);=0A= if(!cocd->throttled)=0A= tty_flip_buffer_push(tty);=0A= up(&cocd->sem);=0A= }=0A= next_message:=0A= co_free_message(input);=0A= input_index =3D 0;=0A= } while(co_get_message(&input, CO_DEVICE_SERIAL));=0A= input =3D 0;=0A= cocd_task_exit:=0A= down(&cocd_sem);=0A= cocd_task_count=3D0;=0A= up(&cocd_sem);=0A= }=0A= =0A= void cocd_interrupt(void)=0A= {=0A= if (!cocd_driver)=0A= return;=0A= //down(&cocd_sem);=0A= //if(!cocd_task_count)=0A= schedule_work(&cocd_work);=0A= //up(&cocd_sem);=0A= }=0A= =0A= int cocd_write(struct tty_struct * tty, int from_user,=0A= const unsigned char *buf, int count)=0A= {=0A= char *allocated_kbuf =3D NULL;=0A= const char *kbuf =3D NULL;=0A= const char *kbuf_scan =3D NULL;=0A= int count_left;=0A= =0A= if (from_user) {=0A= allocated_kbuf =3D kmalloc(count, GFP_KERNEL);=0A= if (copy_from_user(allocated_kbuf, buf, count)) {=0A= kfree(allocated_kbuf);=0A= return -EFAULT;=0A= }=0A= kbuf =3D allocated_kbuf;=0A= } else {=0A= kbuf =3D buf;=0A= }=0A= =0A= kbuf_scan =3D kbuf;=0A= count_left =3D count;=0A= =0A= while (count_left > 0) {=0A= int count_partial =3D count_left;=0A= if (count_partial > 1000)=0A= count_partial =3D 1000;=0A= =0A= co_send_message(CO_MODULE_LINUX,=0A= CO_MODULE_SERIAL0 + tty->index,=0A= CO_PRIORITY_DISCARDABLE,=0A= CO_MESSAGE_TYPE_STRING,=0A= count_partial,=0A= kbuf_scan);=0A= =0A= count_left -=3D count_partial;=0A= kbuf_scan +=3D count_partial;=0A= }=0A= =0A= if (from_user)=0A= kfree(allocated_kbuf);=0A= =0A= return count;=0A= }=0A= =0A= int cocd_write_room(struct tty_struct *tty)=0A= {=0A= struct cocd_tty *cocd =3D NULL;=0A= =0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd)=0A= return 0;=0A= =0A= down(&cocd->sem);=0A= if (cocd->open_count =3D=3D 0) {=0A= /* port was not opened */=0A= up(&cocd->sem);=0A= return 0;=0A= }=0A= =0A= up(&cocd->sem);=0A= return 255;=0A= }=0A= =0A= void cocd_hangup(struct tty_struct *tty)=0A= {=0A= }=0A= =0A= void cocd_throttle(struct tty_struct * tty)=0A= {=0A= struct cocd_tty *cocd;=0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd)=0A= return;=0A= down(&cocd->sem);=0A= cocd->throttled =3D 1;=0A= up(&cocd->sem);=0A= }=0A= =0A= void cocd_unthrottle(struct tty_struct * tty)=0A= {=0A= struct cocd_tty *cocd;=0A= cocd =3D (struct cocd_tty *)tty->driver_data;=0A= if (!cocd)=0A= return;=0A= down(&cocd->sem);=0A= cocd->throttled =3D 0;=0A= up(&cocd->sem);=0A= schedule_work(&cocd_work);=0A= }=0A= =0A= void cocd_flush_buffer(struct tty_struct *tty)=0A= {=0A= }=0A= =0A= void cocd_set_termios(struct tty_struct *tty, struct termios = *old_termios)=0A= {=0A= }=0A= =0A= int cocd_chars_in_buffer(struct tty_struct *tty)=0A= {=0A= return 0;=0A= }=0A= =0A= static struct tty_operations cocd_ops =3D {=0A= .open =3D cocd_open,=0A= .close =3D cocd_close,=0A= .write =3D cocd_write,=0A= .write_room =3D cocd_write_room,=0A= .flush_buffer =3D cocd_flush_buffer,=0A= .throttle =3D cocd_throttle,=0A= .unthrottle =3D cocd_unthrottle,=0A= .hangup =3D cocd_hangup, =0A= .chars_in_buffer =3D cocd_chars_in_buffer, =0A= .set_termios =3D cocd_set_termios,=0A= };=0A= =0A= static struct tty_driver *cocd_driver;=0A= =0A= static void cocd_console_write(struct console *c, const char *string, = unsigned len)=0A= {=0A= }=0A= =0A= static struct tty_driver *cocd_console_device(struct console *c, int = *index)=0A= {=0A= *index =3D c->index;=0A= return cocd_driver;=0A= }=0A= =0A= static int cocd_console_setup(struct console *co, char *options)=0A= {=0A= return(0);=0A= }=0A= =0A= static struct console cocd_cons =3D {=0A= name: "ttyS",=0A= write: cocd_console_write,=0A= device: cocd_console_device,=0A= setup: cocd_console_setup,=0A= flags: CON_PRINTBUFFER,=0A= index: -1,=0A= };=0A= =0A= static int __init cocd_init(void)=0A= {=0A= cocd_driver =3D alloc_tty_driver(CO_MODULE_MAX_SERIAL);=0A= =0A= if (!cocd_driver)=0A= panic("Couldn't allocate cocd driver");=0A= =0A= cocd_driver->owner =3D THIS_MODULE;=0A= cocd_driver->driver_name =3D "Cooperative serial lines";=0A= cocd_driver->name =3D "ttS";=0A= cocd_driver->devfs_name =3D "tts/";=0A= cocd_driver->major =3D TTY_MAJOR;=0A= cocd_driver->minor_start =3D 64;=0A= cocd_driver->type =3D TTY_DRIVER_TYPE_SERIAL;=0A= cocd_driver->subtype =3D 0;=0A= cocd_driver->init_termios =3D tty_std_termios;=0A= cocd_driver->flags =3D 0;=0A= =0A= tty_set_operations(cocd_driver, &cocd_ops);=0A= =0A= if (tty_register_driver(cocd_driver))=0A= panic("Couldn't register cocd driver");=0A= =0A= register_console(&cocd_cons);=0A= =0A= INIT_WORK(&cocd_work, cocd_task, 0);=0A= =0A= return 0;=0A= }=0A= =0A= module_init(cocd_init);=0A= |