hvsi.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,320 行 · 第 1/3 页

C
1,320
字号
		return NULL;	if (overflow > 0) {		pr_debug("%s: got >TTY_THRESHOLD_THROTTLE bytes\n", __FUNCTION__);		datalen = TTY_THRESHOLD_THROTTLE;	}	hvsi_insert_chars(hp, data, datalen);	if (overflow > 0) {		/*		 * we still have more data to deliver, so we need to save off the		 * overflow and send it later		 */		pr_debug("%s: deferring overflow\n", __FUNCTION__);		memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow);		hp->n_throttle = overflow;	}	return hp->tty;}/* * Returns true/false indicating data successfully read from hypervisor. * Used both to get packets for tty connections and to advance the state * machine during console handshaking (in which case tty = NULL and we ignore * incoming data). */static int hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct **flip,		struct tty_struct **hangup){	uint8_t *packet = hp->inbuf;	int chunklen;	*flip = NULL;	*hangup = NULL;	chunklen = hvsi_read(hp, hp->inbuf_end, HVSI_MAX_READ);	if (chunklen == 0)		return 0;	pr_debug("%s: got %i bytes\n", __FUNCTION__, chunklen);	dbg_dump_hex(hp->inbuf_end, chunklen);	hp->inbuf_end += chunklen;	/* handle all completed packets */	while ((packet < hp->inbuf_end) && got_packet(hp, packet)) {		struct hvsi_header *header = (struct hvsi_header *)packet;		if (!is_header(packet)) {			printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index);			/* skip bytes until we find a header or run out of data */			while ((packet < hp->inbuf_end) && (!is_header(packet)))				packet++;			continue;		}		pr_debug("%s: handling %i-byte packet\n", __FUNCTION__,				len_packet(packet));		dbg_dump_packet(packet);		switch (header->type) {			case VS_DATA_PACKET_HEADER:				if (!is_open(hp))					break;				if (hp->tty == NULL)					break; /* no tty buffer to put data in */				*flip = hvsi_recv_data(hp, packet);				break;			case VS_CONTROL_PACKET_HEADER:				*hangup = hvsi_recv_control(hp, packet);				break;			case VS_QUERY_RESPONSE_PACKET_HEADER:				hvsi_recv_response(hp, packet);				break;			case VS_QUERY_PACKET_HEADER:				hvsi_recv_query(hp, packet);				break;			default:				printk(KERN_ERR "hvsi%i: unknown HVSI packet type 0x%x\n",						hp->index, header->type);				dump_packet(packet);				break;		}		packet += len_packet(packet);		if (*hangup) {			pr_debug("%s: hangup\n", __FUNCTION__);			/*			 * we need to send the hangup now before receiving any more data.			 * If we get "data, hangup, data", we can't deliver the second			 * data before the hangup.			 */			break;		}	}	compact_inbuf(hp, packet);	return 1;}static void hvsi_send_overflow(struct hvsi_struct *hp){	pr_debug("%s: delivering %i bytes overflow\n", __FUNCTION__,			hp->n_throttle);	hvsi_insert_chars(hp, hp->throttle_buf, hp->n_throttle);	hp->n_throttle = 0;}/* * must get all pending data because we only get an irq on empty->non-empty * transition */static irqreturn_t hvsi_interrupt(int irq, void *arg, struct pt_regs *regs){	struct hvsi_struct *hp = (struct hvsi_struct *)arg;	struct tty_struct *flip;	struct tty_struct *hangup;	unsigned long flags;	irqreturn_t handled = IRQ_NONE;	int again = 1;	pr_debug("%s\n", __FUNCTION__);	while (again) {		spin_lock_irqsave(&hp->lock, flags);		again = hvsi_load_chunk(hp, &flip, &hangup);		handled = IRQ_HANDLED;		spin_unlock_irqrestore(&hp->lock, flags);		/*		 * we have to call tty_flip_buffer_push() and tty_hangup() outside our		 * spinlock. But we also have to keep going until we've read all the		 * available data.		 */		if (flip) {			/* there was data put in the tty flip buffer */			tty_flip_buffer_push(flip);			flip = NULL;		}		if (hangup) {			tty_hangup(hangup);		}	}	spin_lock_irqsave(&hp->lock, flags);	if (hp->tty && hp->n_throttle			&& (!test_bit(TTY_THROTTLED, &hp->tty->flags))) {		/* we weren't hung up and we weren't throttled, so we can deliver the		 * rest now */		flip = hp->tty;		hvsi_send_overflow(hp);	}	spin_unlock_irqrestore(&hp->lock, flags);	if (flip) {		tty_flip_buffer_push(flip);	}	return handled;}/* for boot console, before the irq handler is running */static int __init poll_for_state(struct hvsi_struct *hp, int state){	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;	for (;;) {		hvsi_interrupt(hp->virq, (void *)hp, NULL); /* get pending data */		if (hp->state == state)			return 0;		mdelay(5);		if (time_after(jiffies, end_jiffies))			return -EIO;	}}/* wait for irq handler to change our state */static int wait_for_state(struct hvsi_struct *hp, int state){	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;	unsigned long timeout;	int ret = 0;	DECLARE_WAITQUEUE(myself, current);	set_current_state(TASK_INTERRUPTIBLE);	add_wait_queue(&hp->stateq, &myself);	for (;;) {		set_current_state(TASK_INTERRUPTIBLE);		if (hp->state == state)			break;		timeout = end_jiffies - jiffies;		if (time_after(jiffies, end_jiffies)) {			ret = -EIO;			break;		}		schedule_timeout(timeout);	}	remove_wait_queue(&hp->stateq, &myself);	set_current_state(TASK_RUNNING);	return ret;}static int hvsi_query(struct hvsi_struct *hp, uint16_t verb){	struct hvsi_query packet __ALIGNED__;	int wrote;	packet.type = VS_QUERY_PACKET_HEADER;	packet.len = sizeof(struct hvsi_query);	packet.seqno = atomic_inc_return(&hp->seqno);	packet.verb = verb;	pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len);	dbg_dump_hex((uint8_t*)&packet, packet.len);	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);	if (wrote != packet.len) {		printk(KERN_ERR "hvsi%i: couldn't send query (%i)!\n", hp->index,			wrote);		return -EIO;	}	return 0;}static int hvsi_get_mctrl(struct hvsi_struct *hp){	int ret;	set_state(hp, HVSI_WAIT_FOR_MCTRL_RESPONSE);	hvsi_query(hp, VSV_SEND_MODEM_CTL_STATUS);	ret = hvsi_wait(hp, HVSI_OPEN);	if (ret < 0) {		printk(KERN_ERR "hvsi%i: didn't get modem flags\n", hp->index);		set_state(hp, HVSI_OPEN);		return ret;	}	pr_debug("%s: mctrl 0x%x\n", __FUNCTION__, hp->mctrl);	return 0;}/* note that we can only set DTR */static int hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl){	struct hvsi_control packet __ALIGNED__;	int wrote;	packet.type = VS_CONTROL_PACKET_HEADER,	packet.seqno = atomic_inc_return(&hp->seqno);	packet.len = sizeof(struct hvsi_control);	packet.verb = VSV_SET_MODEM_CTL;	packet.mask = HVSI_TSDTR;	if (mctrl & TIOCM_DTR)		packet.word = HVSI_TSDTR;	pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len);	dbg_dump_hex((uint8_t*)&packet, packet.len);	wrote = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);	if (wrote != packet.len) {		printk(KERN_ERR "hvsi%i: couldn't set DTR!\n", hp->index);		return -EIO;	}	return 0;}static void hvsi_drain_input(struct hvsi_struct *hp){	uint8_t buf[HVSI_MAX_READ] __ALIGNED__;	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;	while (time_before(end_jiffies, jiffies))		if (0 == hvsi_read(hp, buf, HVSI_MAX_READ))			break;}static int hvsi_handshake(struct hvsi_struct *hp){	int ret;	/*	 * We could have a CLOSE or other data waiting for us before we even try	 * to open; try to throw it all away so we don't get confused. (CLOSE	 * is the first message sent up the pipe when the FSP comes online. We	 * need to distinguish between "it came up a while ago and we're the first	 * user" and "it was just reset before it saw our handshake packet".)	 */	hvsi_drain_input(hp);	set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE);	ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER);	if (ret < 0) {		printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index);		return ret;	}	ret = hvsi_wait(hp, HVSI_OPEN);	if (ret < 0)		return ret;	return 0;}static int hvsi_put_chars(struct hvsi_struct *hp, const char *buf, int count){	struct hvsi_data packet __ALIGNED__;	int ret;	BUG_ON(count > HVSI_MAX_OUTGOING_DATA);	packet.type = VS_DATA_PACKET_HEADER;	packet.seqno = atomic_inc_return(&hp->seqno);	packet.len = count + sizeof(struct hvsi_header);	memcpy(&packet.data, buf, count);	ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);	if (ret == packet.len) {		/* return the number of chars written, not the packet length */		return count;	}	return ret; /* return any errors */}static void hvsi_close_protocol(struct hvsi_struct *hp){	struct hvsi_control packet __ALIGNED__;	packet.type = VS_CONTROL_PACKET_HEADER;	packet.seqno = atomic_inc_return(&hp->seqno);	packet.len = 6;	packet.verb = VSV_CLOSE_PROTOCOL;	pr_debug("%s: sending %i bytes\n", __FUNCTION__, packet.len);	dbg_dump_hex((uint8_t*)&packet, packet.len);	hvc_put_chars(hp->vtermno, (char *)&packet, packet.len);}static int hvsi_open(struct tty_struct *tty, struct file *filp){	struct hvsi_struct *hp;	unsigned long flags;	int line = tty->index;	int ret;	pr_debug("%s\n", __FUNCTION__);	if (line < 0 || line >= hvsi_count)		return -ENODEV;	hp = &hvsi_ports[line];	tty->driver_data = hp;	tty->low_latency = 1; /* avoid throttle/tty_flip_buffer_push race */	spin_lock_irqsave(&hp->lock, flags);	hp->tty = tty;	hp->count++;	atomic_set(&hp->seqno, 0);	h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);	spin_unlock_irqrestore(&hp->lock, flags);	if (hp->flags & HVSI_CONSOLE)		return 0; /* this has already been handshaked as the console */	ret = hvsi_handshake(hp);	if (ret < 0) {		printk(KERN_ERR "%s: HVSI handshaking failed\n", tty->name);		return ret;	}	ret = hvsi_get_mctrl(hp);	if (ret < 0) {		printk(KERN_ERR "%s: couldn't get initial modem flags\n", tty->name);		return ret;	}	ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR);	if (ret < 0) {		printk(KERN_ERR "%s: couldn't set DTR\n", tty->name);		return ret;	}	return 0;}/* wait for hvsi_write_worker to empty hp->outbuf */static void hvsi_flush_output(struct hvsi_struct *hp){	unsigned long end_jiffies = jiffies + HVSI_TIMEOUT;	unsigned long timeout;	DECLARE_WAITQUEUE(myself, current);	set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue(&hp->emptyq, &myself);	for (;;) {		set_current_state(TASK_UNINTERRUPTIBLE);		if (hp->n_outbuf <= 0)			break;		timeout = end_jiffies - jiffies;		if (time_after(jiffies, end_jiffies))			break;		schedule_timeout(timeout);	}	remove_wait_queue(&hp->emptyq, &myself);	set_current_state(TASK_RUNNING);	/* 'writer' could still be pending if it didn't see n_outbuf = 0 yet */	cancel_delayed_work(&hp->writer);	flush_scheduled_work();	/*	 * it's also possible that our timeout expired and hvsi_write_worker	 * didn't manage to push outbuf. poof.	 */	hp->n_outbuf = 0;}static void hvsi_close(struct tty_struct *tty, struct file *filp){	struct hvsi_struct *hp = tty->driver_data;	unsigned long flags;	pr_debug("%s\n", __FUNCTION__);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?