diff options
Diffstat (limited to 'arch/um/drivers/chan_kern.c')
| -rw-r--r-- | arch/um/drivers/chan_kern.c | 196 | 
1 files changed, 87 insertions, 109 deletions
diff --git a/arch/um/drivers/chan_kern.c b/arch/um/drivers/chan_kern.c index 420e2c80079..ca4c7ebfd0a 100644 --- a/arch/um/drivers/chan_kern.c +++ b/arch/um/drivers/chan_kern.c @@ -140,18 +140,18 @@ static int open_chan(struct list_head *chans)  	return err;  } -void chan_enable_winch(struct list_head *chans, struct tty_struct *tty) +void chan_enable_winch(struct chan *chan, struct tty_struct *tty)  { -	struct list_head *ele; -	struct chan *chan; +	if (chan && chan->primary && chan->ops->winch) +		register_winch(chan->fd, tty); +} -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); -		if (chan->primary && chan->output && chan->ops->winch) { -			register_winch(chan->fd, tty); -			return; -		} -	} +static void line_timer_cb(struct work_struct *work) +{ +	struct line *line = container_of(work, struct line, task.work); + +	if (!line->throttled) +		chan_interrupt(line, line->tty, line->driver->read_irq);  }  int enable_chan(struct line *line) @@ -160,6 +160,8 @@ int enable_chan(struct line *line)  	struct chan *chan;  	int err; +	INIT_DELAYED_WORK(&line->task, line_timer_cb); +  	list_for_each(ele, &line->chan_list) {  		chan = list_entry(ele, struct chan, list);  		err = open_one_chan(chan); @@ -183,7 +185,7 @@ int enable_chan(struct line *line)  	return 0;   out_close: -	close_chan(&line->chan_list, 0); +	close_chan(line);  	return err;  } @@ -244,7 +246,7 @@ static void close_one_chan(struct chan *chan, int delay_free_irq)  	chan->fd = -1;  } -void close_chan(struct list_head *chans, int delay_free_irq) +void close_chan(struct line *line)  {  	struct chan *chan; @@ -253,77 +255,50 @@ void close_chan(struct list_head *chans, int delay_free_irq)  	 * state.  Then, the first one opened will have the original state,  	 * so it must be the last closed.  	 */ -	list_for_each_entry_reverse(chan, chans, list) { -		close_one_chan(chan, delay_free_irq); +	list_for_each_entry_reverse(chan, &line->chan_list, list) { +		close_one_chan(chan, 0);  	}  } -void deactivate_chan(struct list_head *chans, int irq) +void deactivate_chan(struct chan *chan, int irq)  { -	struct list_head *ele; - -	struct chan *chan; -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); - -		if (chan->enabled && chan->input) -			deactivate_fd(chan->fd, irq); -	} +	if (chan && chan->enabled) +		deactivate_fd(chan->fd, irq);  } -void reactivate_chan(struct list_head *chans, int irq) +void reactivate_chan(struct chan *chan, int irq)  { -	struct list_head *ele; -	struct chan *chan; - -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); - -		if (chan->enabled && chan->input) -			reactivate_fd(chan->fd, irq); -	} +	if (chan && chan->enabled) +		reactivate_fd(chan->fd, irq);  } -int write_chan(struct list_head *chans, const char *buf, int len, +int write_chan(struct chan *chan, const char *buf, int len,  	       int write_irq)  { -	struct list_head *ele; -	struct chan *chan = NULL;  	int n, ret = 0; -	if (len == 0) +	if (len == 0 || !chan || !chan->ops->write)  		return 0; -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); -		if (!chan->output || (chan->ops->write == NULL)) -			continue; - -		n = chan->ops->write(chan->fd, buf, len, chan->data); -		if (chan->primary) { -			ret = n; -			if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) -				reactivate_fd(chan->fd, write_irq); -		} +	n = chan->ops->write(chan->fd, buf, len, chan->data); +	if (chan->primary) { +		ret = n; +		if ((ret == -EAGAIN) || ((ret >= 0) && (ret < len))) +			reactivate_fd(chan->fd, write_irq);  	}  	return ret;  } -int console_write_chan(struct list_head *chans, const char *buf, int len) +int console_write_chan(struct chan *chan, const char *buf, int len)  { -	struct list_head *ele; -	struct chan *chan;  	int n, ret = 0; -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); -		if (!chan->output || (chan->ops->console_write == NULL)) -			continue; +	if (!chan || !chan->ops->console_write) +		return 0; -		n = chan->ops->console_write(chan->fd, buf, len); -		if (chan->primary) -			ret = n; -	} +	n = chan->ops->console_write(chan->fd, buf, len); +	if (chan->primary) +		ret = n;  	return ret;  } @@ -340,20 +315,24 @@ int console_open_chan(struct line *line, struct console *co)  	return 0;  } -int chan_window_size(struct list_head *chans, unsigned short *rows_out, +int chan_window_size(struct line *line, unsigned short *rows_out,  		      unsigned short *cols_out)  { -	struct list_head *ele;  	struct chan *chan; -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); -		if (chan->primary) { -			if (chan->ops->window_size == NULL) -				return 0; -			return chan->ops->window_size(chan->fd, chan->data, -						      rows_out, cols_out); -		} +	chan = line->chan_in; +	if (chan && chan->primary) { +		if (chan->ops->window_size == NULL) +			return 0; +		return chan->ops->window_size(chan->fd, chan->data, +					      rows_out, cols_out); +	} +	chan = line->chan_out; +	if (chan && chan->primary) { +		if (chan->ops->window_size == NULL) +			return 0; +		return chan->ops->window_size(chan->fd, chan->data, +					      rows_out, cols_out);  	}  	return 0;  } @@ -429,21 +408,15 @@ static int chan_pair_config_string(struct chan *in, struct chan *out,  	return n;  } -int chan_config_string(struct list_head *chans, char *str, int size, +int chan_config_string(struct line *line, char *str, int size,  		       char **error_out)  { -	struct list_head *ele; -	struct chan *chan, *in = NULL, *out = NULL; +	struct chan *in = line->chan_in, *out = line->chan_out; -	list_for_each(ele, chans) { -		chan = list_entry(ele, struct chan, list); -		if (!chan->primary) -			continue; -		if (chan->input) -			in = chan; -		if (chan->output) -			out = chan; -	} +	if (in && !in->primary) +		in = NULL; +	if (out && !out->primary) +		out = NULL;  	return chan_pair_config_string(in, out, str, size, error_out);  } @@ -547,10 +520,14 @@ int parse_chan_pair(char *str, struct line *line, int device,  	char *in, *out;  	if (!list_empty(chans)) { +		line->chan_in = line->chan_out = NULL;  		free_chan(chans);  		INIT_LIST_HEAD(chans);  	} +	if (!str) +		return 0; +  	out = strchr(str, ',');  	if (out != NULL) {  		in = str; @@ -562,6 +539,7 @@ int parse_chan_pair(char *str, struct line *line, int device,  		new->input = 1;  		list_add(&new->list, chans); +		line->chan_in = new;  		new = parse_chan(line, out, device, opts, error_out);  		if (new == NULL) @@ -569,6 +547,7 @@ int parse_chan_pair(char *str, struct line *line, int device,  		list_add(&new->list, chans);  		new->output = 1; +		line->chan_out = new;  	}  	else {  		new = parse_chan(line, str, device, opts, error_out); @@ -578,43 +557,42 @@ int parse_chan_pair(char *str, struct line *line, int device,  		list_add(&new->list, chans);  		new->input = 1;  		new->output = 1; +		line->chan_in = line->chan_out = new;  	}  	return 0;  } -void chan_interrupt(struct list_head *chans, struct delayed_work *task, -		    struct tty_struct *tty, int irq) +void chan_interrupt(struct line *line, struct tty_struct *tty, int irq)  { -	struct list_head *ele, *next; -	struct chan *chan; +	struct chan *chan = line->chan_in;  	int err;  	char c; -	list_for_each_safe(ele, next, chans) { -		chan = list_entry(ele, struct chan, list); -		if (!chan->input || (chan->ops->read == NULL)) -			continue; -		do { -			if (tty && !tty_buffer_request_room(tty, 1)) { -				schedule_delayed_work(task, 1); -				goto out; -			} -			err = chan->ops->read(chan->fd, &c, chan->data); -			if (err > 0) -				tty_receive_char(tty, c); -		} while (err > 0); +	if (!chan || !chan->ops->read) +		goto out; -		if (err == 0) -			reactivate_fd(chan->fd, irq); -		if (err == -EIO) { -			if (chan->primary) { -				if (tty != NULL) -					tty_hangup(tty); -				close_chan(chans, 1); -				return; -			} -			else close_one_chan(chan, 1); +	do { +		if (tty && !tty_buffer_request_room(tty, 1)) { +			schedule_delayed_work(&line->task, 1); +			goto out;  		} +		err = chan->ops->read(chan->fd, &c, chan->data); +		if (err > 0) +			tty_receive_char(tty, c); +	} while (err > 0); + +	if (err == 0) +		reactivate_fd(chan->fd, irq); +	if (err == -EIO) { +		if (chan->primary) { +			if (tty != NULL) +				tty_hangup(tty); +			if (line->chan_out != chan) +				close_one_chan(line->chan_out, 1); +		} +		close_one_chan(chan, 1); +		if (chan->primary) +			return;  	}   out:  	if (tty)  |