Beispiel #1
0
static void rpmsg_tty_cb(struct rpmsg_channel *rpdev, void *data, int len,
                         void *priv, u32 src)
{
    int space;
    unsigned char *cbuf;
    struct rpmsgtty_port *cport = (struct rpmsgtty_port *)priv;

    /* flush the recv-ed none-zero data to tty node */
    if (len == 0)
        return;
 /*
    pr_info("%s lenrcved=%d\n", __FUNCTION__, len);
    print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
                    data, len,  true);
*/                    

    spin_lock_bh(&cport->rx_lock);
    space = tty_prepare_flip_string(&cport->port, &cbuf, len);
    if (space <= 0)
    {
        dev_err(&rpdev->dev, "No memory for tty_prepare_flip_string\n");
        spin_unlock_bh(&cport->rx_lock);
        return;
    }

    if( space != len)
        pr_err("Trunc buffer %d\n", len-space);

    memcpy(cbuf, data, space);
    tty_flip_buffer_push(&cport->port);
    spin_unlock_bh(&cport->rx_lock);
}
Beispiel #2
0
static void smd_tty_read(unsigned long param)
{
	unsigned char *ptr;
	int avail;
	struct smd_tty_info *info = (struct smd_tty_info *)param;
	struct tty_struct *tty = info->tty;

	if (!tty)
		return;

	for (;;) {
		if (test_bit(TTY_THROTTLED, &tty->flags)) break;
		avail = smd_read_avail(info->ch);
		if (avail == 0)
			break;

		avail = tty_prepare_flip_string(tty, &ptr, avail);

		if (smd_read(info->ch, ptr, avail) != avail) {
			
			printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
		}

		wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	
	tty_wakeup(tty);
}
Beispiel #3
0
static void smd_tty_notify(void *priv, unsigned event)
{
	unsigned char *ptr;
	int avail;
	struct smd_tty_info *info = priv;
	struct tty_struct *tty = info->tty;

	if (!tty)
		return;

	if (event != SMD_EVENT_DATA)
		return;

	for (;;) {
		if (test_bit(TTY_THROTTLED, &tty->flags)) break;
		avail = smd_read_avail(info->ch);
		if (avail == 0) break;

		avail = tty_prepare_flip_string(tty, &ptr, avail);

		if (smd_read(info->ch, ptr, avail) != avail) {
			/* shouldn't be possible since we're in interrupt
			** context here and nobody else could 'steal' our
			** characters.
			*/
			printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
		}
                wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	/* XXX only when writable and necessary */
	tty_wakeup(tty);
}
/* Diag char driver has diag packet ready for userspace */
int tty_diag_channel_write(struct usb_diag_ch *diag_ch,
				struct diag_request *d_req)
{
	struct diag_tty_data *tty_data = diag_ch->priv_usb;
	unsigned char *tty_buf;
	int tty_allocated;
	unsigned long flags;
	int cmd_code, subsys_id;

	/* If diag packet is not 1:1 response (perhaps logging packet?),
	   try primary channel */
	if (tty_data == NULL)
		tty_data = &(diag_tty[0]);

	spin_lock_irqsave(&diag_tty_lock, flags);

	if (dbg_ftm_flag == 1) {
		cmd_code = (int)(*(char *)d_req->buf);
		subsys_id = (int)(*(char *)(d_req->buf+1));

		if (cmd_code == ttydiag_dbg_cmd_code &&
				subsys_id == ttydiag_dbg_subsys_id &&
				dbg_tty_minor != -1) {
			/* respond to last tty */
			ttydiag_dbg_cmd_code = 0;
			ttydiag_dbg_subsys_id = 0;
			tty_data = &(diag_tty[dbg_tty_minor]);
			dbg_tty_minor = -1;
		} else {
			tty_data = &(diag_tty[2]);
		}
	}

	if (tty_data->tty == NULL) {
		spin_unlock_irqrestore(&diag_tty_lock, flags);
		return -EIO;
	}

	tty_allocated = tty_prepare_flip_string(&tty_data->port,
						&tty_buf, d_req->length);

	if (tty_allocated < d_req->length) {
		spin_unlock_irqrestore(&diag_tty_lock, flags);
		return -ENOMEM;
	}

	/* Unset active tty for next request diag tool */
	diag_ch->priv_usb = NULL;

	memcpy(tty_buf, d_req->buf, d_req->length);
	tty_flip_buffer_push(&tty_data->port);

	d_req->actual = d_req->length;
	spin_unlock_irqrestore(&diag_tty_lock, flags);

	diag_ch->notify(diag_ch->priv, USB_DIAG_WRITE_DONE, d_req);

	return 0;
}
Beispiel #5
0
static void smd_tty_read(unsigned long param)
{
	unsigned char *ptr;
	int avail;
	struct smd_tty_info *info = (struct smd_tty_info *)param;
	struct tty_struct *tty = tty_port_tty_get(&info->port);
	unsigned long flags;

	if (!tty)
		return;

	for (;;) {
		if (is_in_reset(info)) {
			/* signal TTY clients using TTY_BREAK */
			tty_insert_flip_char(tty, 0x00, TTY_BREAK);
			tty_flip_buffer_push(tty);
			break;
		}

		if (test_bit(TTY_THROTTLED, &tty->flags)) break;
		spin_lock_irqsave(&info->ra_lock, flags);
		avail = smd_read_avail(info->ch);
		if (avail == 0) {
			wake_unlock(&info->ra_wake_lock);
			spin_unlock_irqrestore(&info->ra_lock, flags);
			break;
		}
		spin_unlock_irqrestore(&info->ra_lock, flags);

		if (avail > MAX_TTY_BUF_SIZE)
			avail = MAX_TTY_BUF_SIZE;

		avail = tty_prepare_flip_string(tty, &ptr, avail);
		if (avail <= 0) {
			mod_timer(&info->buf_req_timer,
					jiffies + msecs_to_jiffies(30));
			tty_kref_put(tty);
			return;
		}

		if (smd_read(info->ch, ptr, avail) != avail) {
			/* shouldn't be possible since we're in interrupt
			** context here and nobody else could 'steal' our
			** characters.
			*/
			SMD_TTY_ERR(
				"%s - Possible smd_tty_buffer mismatch for %s",
				__func__, info->ch->name);
		}

		wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	/* XXX only when writable and necessary */
	tty_wakeup(tty);
	tty_kref_put(tty);
}
Beispiel #6
0
static void smd_tty_read(unsigned long param)
{
	unsigned char *ptr;
	int avail;
	struct smd_tty_info *info = (struct smd_tty_info *)param;
	struct tty_struct *tty = info->tty;

	if (!tty)
		return;

	for (;;) {
		unsigned int n = info->tty->index;
		if (is_in_reset(info)) {
			if (n == BT_ACL_IDX || n == BT_CMD_IDX)
				pr_err("%s: BT_IDX read in reset %d \n", __func__, n);
			if ((n != BT_ACL_IDX) && (n != BT_CMD_IDX)) {
			/* signal TTY clients using TTY_BREAK */
				tty_insert_flip_char(tty, 0x00, TTY_BREAK);
				tty_flip_buffer_push(tty);
				break;
			}
		}

		if (test_bit(TTY_THROTTLED, &tty->flags)) break;
		avail = smd_read_avail(info->ch);
		if (avail == 0)
			break;

		if (avail > MAX_TTY_BUF_SIZE)
			avail = MAX_TTY_BUF_SIZE;

		avail = tty_prepare_flip_string(tty, &ptr, avail);
		if (avail <= 0) {
			mod_timer(&info->buf_req_timer,
					jiffies + msecs_to_jiffies(30));
			return;
		}

		if (smd_read(info->ch, ptr, avail) != avail) {
			/* shouldn't be possible since we're in interrupt
			** context here and nobody else could 'steal' our
			** characters.
			*/
			printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
		}

#ifdef CONFIG_HAS_WAKELOCK
		pr_debug("%s: lock wakelock %s\n", __func__, info->wake_lock.name);
#endif
		wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	/* XXX only when writable and necessary */
	tty_wakeup(tty);
}
Beispiel #7
0
static void smd_tty_read(unsigned long param)
{
    unsigned char *ptr;
    int avail;
    struct smd_tty_info *info = (struct smd_tty_info *)param;
    struct tty_struct *tty = info->tty;

    if (!tty)
        return;

    for (;;) {
        if (is_in_reset(info)) {
            /* signal TTY clients using TTY_BREAK */
            tty_insert_flip_char(tty, 0x00, TTY_BREAK);
            tty_flip_buffer_push(tty);
            break;
        }

        if (test_bit(TTY_THROTTLED, &tty->flags))
            break;
        avail = smd_read_avail(info->ch);
        if (avail == 0)
            break;

        if (avail > MAX_TTY_BUF_SIZE)
            avail = MAX_TTY_BUF_SIZE;

        avail = tty_prepare_flip_string(tty, &ptr, avail);
        if (avail <= 0) {
            if (!timer_pending(&info->buf_req_timer)) {
                init_timer(&info->buf_req_timer);
                info->buf_req_timer.expires = jiffies +
                                              ((30 * HZ)/1000);
                info->buf_req_timer.function = buf_req_retry;
                info->buf_req_timer.data = param;
                add_timer(&info->buf_req_timer);
            }
            return;
        }

        if (smd_read(info->ch, ptr, avail) != avail) {
            /* shouldn't be possible since we're in interrupt
            ** context here and nobody else could 'steal' our
            ** characters.
            */
            printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
        }

        wake_lock_timeout(&info->wake_lock, HZ / 2);
        tty_flip_buffer_push(tty);
    }

    /* XXX only when writable and necessary */
    tty_wakeup(tty);
}
static void smd_tty_read(unsigned long param)
{
	unsigned char *ptr;
	int avail;
	struct smd_tty_info *info = (struct smd_tty_info *)param;
	struct tty_struct *tty = info->tty;

	if (!tty)
		return;

	for (;;) {
		if (is_in_reset(info)) {
			
			tty_insert_flip_char(tty, 0x00, TTY_BREAK);
			tty_flip_buffer_push(tty);
			break;
		}

		if (test_bit(TTY_THROTTLED, &tty->flags)) break;
		avail = smd_read_avail(info->ch);
		if (avail == 0)
			break;

		if (avail > MAX_TTY_BUF_SIZE)
			avail = MAX_TTY_BUF_SIZE;

		avail = tty_prepare_flip_string(tty, &ptr, avail);
		if (avail <= 0) {
			mod_timer(&info->buf_req_timer,
					jiffies + msecs_to_jiffies(30));
			return;
		}

		if (smd_read(info->ch, ptr, avail) != avail) {
			printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!");
		
		} else {
			if (get_radio_flag() & 0x0008) {
				int i = 0;
				printk("[RIL]");
				for (i = 0; i< avail; i++)
					printk("%c", *(ptr+i));
			}
		
		}

		wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	
	tty_wakeup(tty);
}
Beispiel #9
0
/** Timer function.
 */
void ec_tty_wakeup(unsigned long data)
{
    ec_tty_t *tty = (ec_tty_t *) data;
    size_t to_recv;

    /* Wake up any process waiting to send data */
    if (tty->wakeup) {
        if (tty->tty) {
#if EC_TTY_DEBUG >= 1
            printk(KERN_INFO PFX "Waking up.\n");
#endif
            tty_wakeup(tty->tty);
        }
        tty->wakeup = 0;
    }

    /* Push received data into TTY core. */
    to_recv = ec_tty_rx_size(tty);
    if (to_recv && tty->tty) {
        unsigned char *cbuf;
        int space = tty_prepare_flip_string(tty->tty, &cbuf, to_recv);

        if (space < to_recv) {
            printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n",
                    to_recv, space);
        }

        if (space < 0) {
            to_recv = 0;
        } else {
            to_recv = space;
        }

        if (to_recv) {
            unsigned int i;

#if EC_TTY_DEBUG >= 1
            printk(KERN_INFO PFX "Pushing %u bytes to TTY core.\n", to_recv);
#endif

            for (i = 0; i < to_recv; i++) {
                cbuf[i] = tty->rx_buffer[tty->rx_read_idx];
                tty->rx_read_idx =
                    (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE;
            }
            tty_flip_buffer_push(tty->tty);
        }
    }

    tty->timer.expires += 1;
    add_timer(&tty->timer);
}
static void aircable_read(struct work_struct *work)
{
	struct aircable_private *priv =
		container_of(work, struct aircable_private, rx_work);
	struct usb_serial_port *port = priv->port;
	struct tty_struct *tty;
	unsigned char *data;
	int count;
	if (priv->rx_flags & THROTTLED) {
		if (priv->rx_flags & ACTUALLY_THROTTLED)
			schedule_work(&priv->rx_work);
		return;
	}

	/* By now I will flush data to the tty in packages of no more than
	 * 64 bytes, to ensure I do not get throttled.
	 * Ask USB mailing list for better aproach.
	 */
	tty = tty_port_tty_get(&port->port);

	if (!tty) {
		schedule_work(&priv->rx_work);
		dev_err(&port->dev, "%s - No tty available\n", __func__);
		return ;
	}

	count = min(64, serial_buf_data_avail(priv->rx_buf));

	if (count <= 0)
		goto out; /* We have finished sending everything. */

	tty_prepare_flip_string(tty, &data, count);
	if (!data) {
		dev_err(&port->dev, "%s- kzalloc(%d) failed.",
							__func__, count);
		goto out;
	}

	serial_buf_get(priv->rx_buf, data, count);

	tty_flip_buffer_push(tty);

	if (serial_buf_data_avail(priv->rx_buf))
		schedule_work(&priv->rx_work);
out:		
	tty_kref_put(tty);
	return;
}
static int gdun_rx_string(struct sk_buff *skb)
{
	int avail = 0;
	if (gdun_tty != NULL) {
		unsigned char *ptr;

		avail = tty_prepare_flip_string(gdun_tty, &ptr, skb->len);

		if (avail <= 0) {
			pr_err("tty_prepare_flip_string err\n");
		} else {
			memcpy(ptr, skb->data, avail);
			tty_flip_buffer_push(gdun_tty);
		}
	}
	return avail;
}
Beispiel #12
0
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
	struct goldfish_tty *qtty = dev_id;
	void __iomem *base = qtty->base;
	unsigned long address;
	unsigned char *buf;
	u32 count;

	count = readl(base + GOLDFISH_TTY_REG_BYTES_READY);
	if (count == 0)
		return IRQ_NONE;

	count = tty_prepare_flip_string(&qtty->port, &buf, count);

	address = (unsigned long)(void *)buf;
	goldfish_tty_rw(qtty, address, count, 0);

	tty_schedule_flip(&qtty->port);
	return IRQ_HANDLED;
}
Beispiel #13
0
static irqreturn_t goldfish_tty_interrupt(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
	struct goldfish_tty *qtty = &goldfish_ttys[pdev->id];
	uint32_t base = qtty->base;
	unsigned long irq_flags;
	unsigned char *buf;
	uint32_t count;

	count = readl(base + GOLDFISH_TTY_BYTES_READY);
	if(count == 0) {
		return IRQ_NONE;
	}
	count = tty_prepare_flip_string(qtty->tty, &buf, count);
	spin_lock_irqsave(&qtty->lock, irq_flags);
	writel(buf, base + GOLDFISH_TTY_DATA_PTR);
	writel(count, base + GOLDFISH_TTY_DATA_LEN);
	writel(GOLDFISH_TTY_CMD_READ_BUFFER, base + GOLDFISH_TTY_CMD);
	spin_unlock_irqrestore(&qtty->lock, irq_flags);
	tty_schedule_flip(qtty->tty);
	return IRQ_HANDLED;
}
Beispiel #14
0
/*
** Routine for handling received data for tty drivers
*/
static void RIOReceive(struct rio_info *p, struct Port *PortP)
{
	struct tty_struct *TtyP;
	unsigned short transCount;
	struct PKT __iomem *PacketP;
	register unsigned int DataCnt;
	unsigned char __iomem *ptr;
	unsigned char *buf;
	int copied = 0;

	static int intCount, RxIntCnt;

	/*
	 ** The receive data process is to remove packets from the
	 ** PHB until there aren't any more or the current cblock
	 ** is full. When this occurs, there will be some left over
	 ** data in the packet, that we must do something with.
	 ** As we haven't unhooked the packet from the read list
	 ** yet, we can just leave the packet there, having first
	 ** made a note of how far we got. This means that we need
	 ** a pointer per port saying where we start taking the
	 ** data from - this will normally be zero, but when we
	 ** run out of space it will be set to the offset of the
	 ** next byte to copy from the packet data area. The packet
	 ** length field is decremented by the number of bytes that
	 ** we successfully removed from the packet. When this reaches
	 ** zero, we reset the offset pointer to be zero, and free
	 ** the packet from the front of the queue.
	 */

	intCount++;

	TtyP = PortP->gs.tty;
	if (!TtyP) {
		rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: tty is null. \n");
		return;
	}

	if (PortP->State & RIO_THROTTLE_RX) {
		rio_dprintk(RIO_DEBUG_INTR, "RIOReceive: Throttled. Can't handle more input.\n");
		return;
	}

	if (PortP->State & RIO_DELETED) {
		while (can_remove_receive(&PacketP, PortP)) {
			remove_receive(PortP);
			put_free_end(PortP->HostP, PacketP);
		}
	} else {
		/*
		 ** loop, just so long as:
		 **   i ) there's some data ( i.e. can_remove_receive )
		 **  ii ) we haven't been blocked
		 ** iii ) there's somewhere to put the data
		 **  iv ) we haven't outstayed our welcome
		 */
		transCount = 1;
		while (can_remove_receive(&PacketP, PortP)
		       && transCount) {
			RxIntCnt++;

			/*
			 ** check that it is not a command!
			 */
			if (readb(&PacketP->len) & PKT_CMD_BIT) {
				rio_dprintk(RIO_DEBUG_INTR, "RIO: unexpected command packet received on PHB\n");
				/*      rio_dprint(RIO_DEBUG_INTR, (" sysport   = %d\n", p->RIOPortp->PortNum)); */
				rio_dprintk(RIO_DEBUG_INTR, " dest_unit = %d\n", readb(&PacketP->dest_unit));
				rio_dprintk(RIO_DEBUG_INTR, " dest_port = %d\n", readb(&PacketP->dest_port));
				rio_dprintk(RIO_DEBUG_INTR, " src_unit  = %d\n", readb(&PacketP->src_unit));
				rio_dprintk(RIO_DEBUG_INTR, " src_port  = %d\n", readb(&PacketP->src_port));
				rio_dprintk(RIO_DEBUG_INTR, " len	   = %d\n", readb(&PacketP->len));
				rio_dprintk(RIO_DEBUG_INTR, " control   = %d\n", readb(&PacketP->control));
				rio_dprintk(RIO_DEBUG_INTR, " csum	   = %d\n", readw(&PacketP->csum));
				rio_dprintk(RIO_DEBUG_INTR, "	 data bytes: ");
				for (DataCnt = 0; DataCnt < PKT_MAX_DATA_LEN; DataCnt++)
					rio_dprintk(RIO_DEBUG_INTR, "%d\n", readb(&PacketP->data[DataCnt]));
				remove_receive(PortP);
				put_free_end(PortP->HostP, PacketP);
				continue;	/* with next packet */
			}

			/*
			 ** How many characters can we move 'upstream' ?
			 **
			 ** Determine the minimum of the amount of data
			 ** available and the amount of space in which to
			 ** put it.
			 **
			 ** 1.        Get the packet length by masking 'len'
			 **   for only the length bits.
			 ** 2.        Available space is [buffer size] - [space used]
			 **
			 ** Transfer count is the minimum of packet length
			 ** and available space.
			 */

			transCount = tty_buffer_request_room(TtyP, readb(&PacketP->len) & PKT_LEN_MASK);
			rio_dprintk(RIO_DEBUG_REC, "port %d: Copy %d bytes\n", PortP->PortNum, transCount);
			/*
			 ** To use the following 'kkprintfs' for debugging - change the '#undef'
			 ** to '#define', (this is the only place ___DEBUG_IT___ occurs in the
			 ** driver).
			 */
			ptr = (unsigned char __iomem *) PacketP->data + PortP->RxDataStart;

			tty_prepare_flip_string(TtyP, &buf, transCount);
			rio_memcpy_fromio(buf, ptr, transCount);
			PortP->RxDataStart += transCount;
			writeb(readb(&PacketP->len)-transCount, &PacketP->len);
			copied += transCount;



			if (readb(&PacketP->len) == 0) {
				/*
				 ** If we have emptied the packet, then we can
				 ** free it, and reset the start pointer for
				 ** the next packet.
				 */
				remove_receive(PortP);
				put_free_end(PortP->HostP, PacketP);
				PortP->RxDataStart = 0;
			}
		}
	}
	if (copied) {
		rio_dprintk(RIO_DEBUG_REC, "port %d: pushing tty flip buffer: %d total bytes copied.\n", PortP->PortNum, copied);
		tty_flip_buffer_push(TtyP);
	}

	return;
}
Beispiel #15
0
static void atcmd_tty_read(struct work_struct *work)
{
	struct atcmd_tty_info *info =
		container_of(work, struct atcmd_tty_info, work.work);
	struct buf_fifo *read_buffer;
	struct tty_struct *tty = info->tty;
	unsigned char *ptr;
	int n_read;
	int avail, remain, buf_size;
	char *read_addr;

	read_buffer = info->read_buffer;

	mutex_lock(read_buffer->lock);

	read_addr = read_buffer->buf_addr + read_buffer->tail;
	buf_size = read_buffer->size;


	if (!tty) {
		mutex_unlock(read_buffer->lock);
		return;
	}
	
	for (;;) {
		if (test_bit(TTY_THROTTLED, &tty->flags))
			break;
		
		n_read = buf_size - get_free_space(read_buffer);
		if (n_read == 0)
			break;

		avail = tty_prepare_flip_string(tty, &ptr, n_read);
		if (avail <= 0) {
			schedule_delayed_work(&info->work, (30 / 1000) * HZ);
			mutex_unlock(read_buffer->lock);
			return;
		}

		if (read_buffer->head > read_buffer->tail) {
			memcpy(ptr, read_addr, avail);
			read_buffer->tail += avail;
		} else {
			int read_len = avail;
			remain = buf_size - read_buffer->tail;
			if (remain < avail) {
				memcpy(ptr, read_addr, remain);
				ptr += remain;
				read_len -= remain;
				read_buffer->tail = 0;
				read_addr = read_buffer->buf_addr;
			}
			memcpy(ptr, read_addr, read_len);
			read_buffer->tail += read_len;
			read_buffer->tail %= buf_size;
		}
		read_buffer->count -= avail;

		wake_lock_timeout(&info->wake_lock, HZ / 2);
		tty_flip_buffer_push(tty);
	}

	mutex_unlock(read_buffer->lock);

	if (info->other_tty->tty)
		tty_wakeup(info->other_tty->tty);

	return;
}
Beispiel #16
0
static void smd_tty_notify(void *priv, unsigned event)
{
	struct smd_tty_info *info = priv;
	unsigned long flags;
	unsigned char *ptr;

	switch (event) {
	case SMD_EVENT_DATA:
		spin_lock_irqsave(&info->reset_lock, flags);
		if (!info->is_open) {
			spin_unlock_irqrestore(&info->reset_lock, flags);
			break;
		}
		spin_unlock_irqrestore(&info->reset_lock, flags);
		/* There may be clients (tty framework) that are blocked
		 * waiting for space to write data, so if a possible read
		 * interrupt came in wake anyone waiting and disable the
		 * interrupts
		 */
		if (smd_write_avail(info->ch)) {
			smd_disable_read_intr(info->ch);
			if (info->tty) {
				unsigned int n = info->tty->index;
				wake_up_interruptible(&info->tty->write_wait);

				/* use pm_qos for BT performance */
				if (n == BT_ACL_IDX || n == BT_CMD_IDX)
					schedule_work(&pm_qos_set_work);
			}
		}
		tasklet_hi_schedule(&info->tty_tsklt);
		break;

	case SMD_EVENT_OPEN:
		if (is_in_reset(info)) {
			unsigned int n = info->tty->index;
			if (n == BT_CMD_IDX) {
				pr_err("%s:  BT_CMD_IDX Sending hardware error event to stack\n", __func__);
				tty_prepare_flip_string(info->tty, &ptr, 0x03);
				ptr[0] = 0x10;
				ptr[1] = 0x01;
				ptr[2] = 0x0A;
				tty_flip_buffer_push(info->tty);
			}
		}
		spin_lock_irqsave(&info->reset_lock, flags);
		info->in_reset = 0;
		info->in_reset_updated = 1;
		info->is_open = 1;
		wake_up_interruptible(&info->ch_opened_wait_queue);
		spin_unlock_irqrestore(&info->reset_lock, flags);

		break;

	case SMD_EVENT_CLOSE:
		spin_lock_irqsave(&info->reset_lock, flags);
		info->in_reset = 1;
		info->in_reset_updated = 1;
		info->is_open = 0;
		wake_up_interruptible(&info->ch_opened_wait_queue);
		spin_unlock_irqrestore(&info->reset_lock, flags);
		/* schedule task to send TTY_BREAK */
		tasklet_hi_schedule(&info->tty_tsklt);

		if (info->tty->index == LOOPBACK_IDX)
			schedule_delayed_work(&loopback_work,
					msecs_to_jiffies(1000));
		break;
	}
}
int atcmd_write_toatd(struct gdata_port *port, struct sk_buff *skb)
{
    struct tty_struct *tty;
    unsigned char *ptr;
    int avail;
    char *cmd;
    int i;

    pr_debug("%s\n", __func__);

    tty = port->tty;
    if (!tty)
        return -ENODEV;

    avail = skb->len;
    if (avail == 0)
        return -EINVAL;

    ptr = skb->data + avail - 1;
    if (strncasecmp(skb->data, "AT", 2) ||
        !(*ptr == '\r' || *ptr == '\n' || *ptr == '\0')) {
        return -EINVAL;
    }

    cmd = kstrdup(skb->data + 2, GFP_ATOMIC);
    if (!cmd) {
        pr_debug("%s: ENOMEM\n", __func__);
        return -ENOMEM;
    }
    if ((ptr = strchr(cmd, '=')) ||
        (ptr = strchr(cmd, '?')) ||
        (ptr = strchr(cmd, '\r')) ) {
        *ptr = '\0';
    }

    if (*cmd != '\0') {
        for (i = 0; at_table[i] != NULL; i++) {
            if (!strcasecmp(cmd, at_table[i])) {
                kfree(cmd);

                if (!test_bit(CH_OPENED, &port->bridge_sts)) {
                    /* signal TTY clients using TTY_BREAK */
                    tty_insert_flip_char(tty, 0x00, TTY_BREAK);
                    tty_flip_buffer_push(tty);
                    break;
                } else {
                    avail = tty_prepare_flip_string(tty, &ptr, avail);
                    if (avail <= 0) {
                        return -EBUSY;
                    }

#ifdef VERBOSE_DEBUG
                    print_hex_dump(KERN_DEBUG, "toatd:", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, 1);
#endif

                    memcpy(ptr, skb->data, avail);
                    dev_kfree_skb_any(skb);

                    tty_flip_buffer_push(tty);
                }

                /* XXX only when writable and necessary */
                tty_wakeup(tty);

                return 0;
            }
        }
    }

    kfree(cmd);
    return -ENOENT;
}