Exemplo n.º 1
0
int modem_request_mmio(struct modemctl *mc)
{
    unsigned long flags;
    int ret;
    spin_lock_irqsave(&mc->lock, flags);
    mc->mmio_req_count++;
    ret = mc->mmio_owner;
    if (!ret) {
        if (mmio_sem(mc) == 1) {
            /* surprise! we already have control */
            ret = mc->mmio_owner = 1;
            wake_up(&mc->wq);
            modem_update_state(mc);
            MODEM_COUNT(mc,request_no_wait);
        } else {
            /* ask the modem for mmio access */
            if (modem_running(mc))
                modem_request_sem(mc);
            MODEM_COUNT(mc,request_wait);
        }
    } else {
        MODEM_COUNT(mc,request_no_wait);
    }
    /* TODO: timer to retry? */
    spin_unlock_irqrestore(&mc->lock, flags);
    return ret;
}
Exemplo n.º 2
0
void modem_release_mmio(struct modemctl *mc, unsigned bits)
{
    unsigned long flags;
    spin_lock_irqsave(&mc->lock, flags);
    mc->mmio_req_count--;
    mc->mmio_signal_bits |= bits;
    if ((mc->mmio_req_count == 0) && modem_running(mc)) {
        if (mc->mmio_bp_request) {
            mc->mmio_bp_request = 0;
            writel(0, mc->mmio + OFF_SEM);
            writel(MB_COMMAND | MB_VALID | MBC_RES_SEM,
                   mc->mmio + OFF_MBOX_AP);
            MODEM_COUNT(mc,release_bp_waiting);
        } else if (mc->mmio_signal_bits) {
            writel(0, mc->mmio + OFF_SEM);
            writel(MB_VALID | mc->mmio_signal_bits,
                   mc->mmio + OFF_MBOX_AP);
            MODEM_COUNT(mc,release_bp_signaled);
        } else {
            MODEM_COUNT(mc,release_no_action);
        }
        mc->mmio_owner = 0;
        mc->mmio_signal_bits = 0;
    }
    spin_unlock_irqrestore(&mc->lock, flags);
}
Exemplo n.º 3
0
static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
{
	struct vnet *vn = netdev_priv(ndev);
	struct modemctl *mc = vn->mc;
	unsigned long flags;

	spin_lock_irqsave(&mc->lock, flags);
	if (readl(mc->mmio + OFF_SEM) & 1) {
		/* if we happen to hold the hw mmio sem, transmit NOW */
		if (handle_raw_tx(mc, skb)) {
			wake_lock(&mc->ip_tx_wakelock);
			skb_queue_tail(&vn->txq, skb);
		} else {
			MODEM_COUNT(mc, tx_no_delay);
		}
		if (!mc->mmio_owner) {
			/* if we don't own the semaphore, immediately
			 * give it back to the modem and signal the modem
			 * to process the packet
			 */
			writel(0, mc->mmio + OFF_SEM);
			writel(MB_VALID | MBD_SEND_RAW,
			       mc->mmio + OFF_MBOX_AP);
			MODEM_COUNT(mc, tx_bp_signaled);
		}
	} else {
		/* otherwise request the hw mmio sem and queue */
		modem_request_sem(mc);
		skb_queue_tail(&vn->txq, skb);
		MODEM_COUNT(mc, tx_queued);
	}
	spin_unlock_irqrestore(&mc->lock, flags);

	return NETDEV_TX_OK;
}
Exemplo n.º 4
0
static void handle_raw_rx(struct modemctl *mc)
{
	struct raw_hdr raw;
	struct sk_buff *skb = NULL;
	int recvdata = 0;

	/* process inbound packets */
	while (fifo_read(&mc->raw_rx, &raw, sizeof(raw)) == sizeof(raw)) {
		struct net_device *dev = mc->ndev;
		unsigned sz = raw.len - (sizeof(raw) - 1);

		if (unlikely(raw.channel != RAW_CH_VNET0)) {
			MODEM_COUNT(mc, rx_unknown);
			pr_err("[VNET] unknown channel %d\n", raw.channel);
			if (fifo_skip(&mc->raw_rx, sz + 1) != (sz + 1))
				goto purge_raw_fifo;
			continue;
		}

		skb = dev_alloc_skb(sz + NET_IP_ALIGN);
		if (skb == NULL) {
			MODEM_COUNT(mc, rx_dropped);
			/* TODO: consider timer + retry instead of drop? */
			pr_err("[VNET] cannot alloc %d byte packet\n", sz);
			if (fifo_skip(&mc->raw_rx, sz + 1) != (sz + 1))
				goto purge_raw_fifo;
			continue;
		}
		skb->dev = dev;
		skb_reserve(skb, NET_IP_ALIGN);

		if (fifo_read(&mc->raw_rx, skb_put(skb, sz), sz) != sz)
			goto purge_raw_fifo;
		if (fifo_skip(&mc->raw_rx, 1) != 1)
			goto purge_raw_fifo;

		skb->protocol = __constant_htons(ETH_P_IP);
		dev->stats.rx_packets++;
		dev->stats.rx_bytes += skb->len;

		netif_rx(skb);
		recvdata = 1;
		MODEM_COUNT(mc, rx_received);
	}

	if (recvdata)
		wake_lock_timeout(&mc->ip_rx_wakelock, HZ * 2);
	return;

purge_raw_fifo:
	if (skb)
		dev_kfree_skb_irq(skb);
	pr_err("[VNET] purging raw rx fifo!\n");
	fifo_purge(&mc->raw_tx);
	MODEM_COUNT(mc, rx_purged);
}
Exemplo n.º 5
0
/* must be called with pipe->tx_lock held */
static int modem_pipe_send(struct m_pipe *pipe, struct modem_io *io)
{
	char hdr[M_PIPE_MAX_HDR];
	unsigned size;
	int ret;

	io->magic = 0xCAFECAFE;

	ret = pipe->push_header(io, hdr);
	if (ret)
		return ret;

	size = io->datasize + pipe->header_size;

	if (size > 0x1000 /*pipe->tx->size*/)
	{
		pr_err ("Trying to send bigger than 4kB frame - MULTIPACKET not implemented yet.\n");
		return -EINVAL;
	}

	for (;;) {
		ret = modem_acquire_mmio(pipe->mc);
		if (ret)
			return ret;

		modem_update_pipe(pipe);

		if (pipe->tx->avail >= size) {
			fifo_write(pipe->tx, hdr, pipe->header_size);
			fifo_move_head(pipe->tx, pipe->header_size);
			fifo_write_user(pipe->tx, io->data, io->datasize);
			fifo_move_head(pipe->tx, SIZ_PACKET_BUFSIZE);
			modem_update_pipe(pipe);
			modem_release_mmio(pipe->mc, pipe->tx->bits);
			MODEM_COUNT(pipe->mc, pipe_tx);
			return 0;
		}

		pr_info("modem_pipe_send: wait for space\n");
		MODEM_COUNT(pipe->mc, pipe_tx_delayed);
		modem_release_mmio(pipe->mc, 0);

		ret = wait_event_interruptible(pipe->mc->wq,
					       (pipe->tx->avail >= size)
					       || modem_offline(pipe->mc));
		if (ret)
			return ret;
	}
}
Exemplo n.º 6
0
/* must be called with pipe->tx_lock held */
static int modem_pipe_send(struct m_pipe *pipe, struct modem_io *io)
{
	char hdr[M_PIPE_MAX_HDR];
	static char ftr = 0x7e;
	unsigned size;
	int ret;

	ret = pipe->push_header(io, hdr);
	if (ret)
		return ret;

	size = io->size + pipe->header_size + 1;

	if (io->size > 0x10000000)
		return -EINVAL;
	if (size >= (pipe->tx->size - 1))
		return -EINVAL;

	for (;;) {
		ret = modem_acquire_mmio(pipe->mc);
		if (ret)
			return ret;

		modem_update_pipe(pipe);

		if (pipe->tx->avail >= size) {
			fifo_write(pipe->tx, hdr, pipe->header_size);
			fifo_write_user(pipe->tx, io->data, io->size);
			fifo_write(pipe->tx, &ftr, 1);
			modem_update_pipe(pipe);
			modem_release_mmio(pipe->mc, pipe->tx->bits);
			MODEM_COUNT(pipe->mc, pipe_tx);
			return 0;
		}

		pr_info("modem_pipe_send: wait for space\n");
		MODEM_COUNT(pipe->mc, pipe_tx_delayed);
		modem_release_mmio(pipe->mc, 0);

		ret = wait_event_interruptible_timeout(
			pipe->mc->wq,
			(pipe->tx->avail >= size) || modem_offline(pipe->mc),
			5 * HZ);
		if (ret == 0)
			return -ENODEV;
		if (ret < 0)
			return ret;
	}
}
Exemplo n.º 7
0
static long modemctl_ioctl(struct file *filp,
			   unsigned int cmd, unsigned long arg)
{
	struct modemctl *mc = filp->private_data;
	int ret;

	mutex_lock(&mc->ctl_lock);
	switch (cmd) {
	case IOCTL_MODEM_RESET:
		ret = modem_reset(mc);
		MODEM_COUNT(mc,resets);
		break;
	case IOCTL_MODEM_START:
		ret = modem_start(mc, 0);
		break;
	case IOCTL_MODEM_RAMDUMP:
		ret = modem_start(mc, 1);
		break;
	case IOCTL_MODEM_OFF:
		ret = modem_off(mc);
		break;
	default:
		ret = -EINVAL;
	}
	mutex_unlock(&mc->ctl_lock);
	pr_info("modemctl_ioctl() %d\n", ret);
	return ret;
}
Exemplo n.º 8
0
int handle_raw_tx(struct modemctl *mc, struct sk_buff *skb)
{
	struct raw_hdr raw;
	unsigned char ftr = 0x7e;
	unsigned sz;

	sz = skb->len + sizeof(raw) + 1;

	if (fifo_space(&mc->raw_tx) < sz) {
		MODEM_COUNT(mc, tx_fifo_full);
		return -1;
	}

	raw.start = 0x7f;
	raw.len = 6 + skb->len;
	raw.channel = RAW_CH_VNET0;
	raw.control = 0;

	fifo_write(&mc->raw_tx, &raw, sizeof(raw));
	fifo_write(&mc->raw_tx, skb->data, skb->len);
	fifo_write(&mc->raw_tx, &ftr, 1);

	mc->ndev->stats.tx_packets++;
	mc->ndev->stats.tx_bytes += skb->len;

	mc->mmio_signal_bits |= MBD_SEND_RAW;

	dev_kfree_skb_irq(skb);
	return 0;
}
Exemplo n.º 9
0
static long modemctl_ioctl(struct file *filp,
                           unsigned int cmd, unsigned long arg)
{
    struct modemctl *mc = filp->private_data;
    struct dpram_firmware fw;
    struct stat_info *pst;
    int ret = 0;

    mutex_lock(&mc->ctl_lock);
    switch (cmd) {
    case IOCTL_MODEM_RESET:
        ret = modem_reset(mc);
        MODEM_COUNT(mc,resets);
        break;
    case IOCTL_MODEM_START:
        ret = modem_start(mc, 0);
        break;
    case IOCTL_MODEM_RAMDUMP:
        ret = modem_start(mc, 1);
        break;
    case IOCTL_MODEM_OFF:
        ret = modem_off(mc);
        break;

    /* CDMA modem update in recovery mode */
    case IOCTL_MODEM_FW_UPDATE:
        pr_info("IOCTL_MODEM_FW_UPDATE\n");
        if (arg == '\0') {
            pr_err("No firmware");
            break;
        }

        ret = copy_from_user((void *)&fw, (void *)arg, sizeof(fw));
        if (ret < 0) {
            pr_err("copy from user failed!");
            ret = -EINVAL;
        } else if (dpram_process_modem_update(mc, &fw) < 0) {
            pr_err("firmware write failed\n");
            ret = -EIO;
        }
        break;
    case IOCTL_MODEM_CHK_STAT:
        pst = (struct stat_info *)arg;
        if (mc->is_modem_delta_update)
            ret = dpram_chk_delta_update(mc, &(pst->pct), pst->msg);
        else
            ret = dpram_chk_full_update(mc, &(pst->pct), pst->msg);
        break;
    case IOCTL_MODEM_PWROFF:
        pr_info("IOCTL_MODEM_PWROFF\n");
        dpram_modem_pwroff(mc);
        break;
    default:
        ret = -EINVAL;
    }
    mutex_unlock(&mc->ctl_lock);
    pr_info("modemctl_ioctl() %d\n", ret);
    return ret;
}
Exemplo n.º 10
0
/* must be called with pipe->rx_lock held */
static int modem_pipe_recv(struct m_pipe *pipe, struct modem_io *io)
{
	int ret;

	ret = modem_acquire_mmio(pipe->mc);
	if (ret)
		return ret;

	ret = modem_pipe_read(pipe, io);

	modem_update_pipe(pipe);

	if ((ret != 0) && (ret != -EAGAIN)) {
		pr_err("[MODEM] purging %s fifo\n", pipe->dev.name);
		fifo_purge(pipe->rx);
		MODEM_COUNT(pipe->mc, pipe_rx_purged);
	} else if (ret == 0) {
		MODEM_COUNT(pipe->mc, pipe_rx);
	}

	modem_release_mmio(pipe->mc, 0);

	return ret;
}
Exemplo n.º 11
0
static irqreturn_t modemctl_mbox_irq_handler(int irq, void *_mc)
{
    struct modemctl *mc = _mc;
    unsigned cmd;
    unsigned long flags;

    cmd = readl(mc->mmio + OFF_MBOX_BP);

    if (unlikely(mc->status != MODEM_RUNNING)) {
        modemctl_handle_offline(mc, cmd);
        return IRQ_HANDLED;
    }

    if (!(cmd & MB_VALID)) {
        if (cmd == MODEM_MSG_LOGDUMP_DONE) {
            pr_info("modem: logdump done!\n");
            mc->logdump_data = 1;
            wake_up(&mc->wq);
        } else {
            pr_info("modem: what is %08x\n",cmd);
        }
        return IRQ_HANDLED;
    }

    spin_lock_irqsave(&mc->lock, flags);

    if (cmd & MB_COMMAND) {
        switch (cmd & 15) {
        case MBC_REQ_SEM:
            if (mmio_sem(mc) == 0) {
                /* Sometimes the modem may ask for the
                 * sem when it already owns it.  Humor
                 * it and ack that request.
                 */
                writel(MB_COMMAND | MB_VALID | MBC_RES_SEM,
                       mc->mmio + OFF_MBOX_AP);
                MODEM_COUNT(mc,bp_req_confused);
            } else if (mc->mmio_req_count == 0) {
                /* No references? Give it to the modem. */
                modem_update_state(mc);
                mc->mmio_owner = 0;
                writel(0, mc->mmio + OFF_SEM);
                writel(MB_COMMAND | MB_VALID | MBC_RES_SEM,
                       mc->mmio + OFF_MBOX_AP);
                MODEM_COUNT(mc,bp_req_instant);
                goto done;
            } else {
                /* Busy now, remember the modem needs it. */
                mc->mmio_bp_request = 1;
                MODEM_COUNT(mc,bp_req_delayed);
                break;
            }
        case MBC_RES_SEM:
            break;
        case MBC_PHONE_START:
            /* TODO: should we avoid sending any other messages
             * to the modem until this message is received and
             * acknowledged?
             */
            writel(MB_COMMAND | MB_VALID |
                   MBC_INIT_END | CP_BOOT_AIRPLANE | AP_OS_ANDROID,
                   mc->mmio + OFF_MBOX_AP);

            /* TODO: probably unsafe to send this back-to-back
             * with the INIT_END message.
             */
            /* if somebody is waiting for mmio access... */
            if (mc->mmio_req_count)
                modem_request_sem(mc);
            break;
        case MBC_RESET:
            pr_err("$$$ MODEM RESET $$$\n");
            mc->status = MODEM_CRASHED;
            wake_up(&mc->wq);
            break;
        case MBC_ERR_DISPLAY: {
            char buf[SIZ_ERROR_MSG + 1];
            int i;
            pr_err("$$$ MODEM ERROR $$$\n");
            mc->status = MODEM_CRASHED;
            wake_up(&mc->wq);
            memcpy(buf, mc->mmio + OFF_ERROR_MSG, SIZ_ERROR_MSG);
            for (i = 0; i < SIZ_ERROR_MSG; i++)
                if ((buf[i] < 0x20) || (buf[1] > 0x7e))
                    buf[i] = 0x20;
            buf[i] = 0;
            i--;
            while ((i > 0) && (buf[i] == 0x20))
                buf[i--] = 0;
            pr_err("$$$ %s $$$\n", buf);
            break;
        }
        case MBC_SUSPEND:
            break;
        case MBC_RESUME:
            break;
        }
    }

    /* On *any* interrupt from the modem it may have given
     * us ownership of the mmio hw semaphore.  If that
     * happens, we should claim the semaphore if we have
     * threads waiting for it and we should process any
     * messages that the modem has enqueued in its fifos
     * by calling modem_handle_io().
     */
    if (mmio_sem(mc) == 1) {
        if (!mc->mmio_owner) {
            modem_update_state(mc);
            if (mc->mmio_req_count) {
                mc->mmio_owner = 1;
                wake_up(&mc->wq);
            }
        }

        modem_handle_io(mc);

        /* If we have a signal to send and we're not
         * hanging on to the mmio hw semaphore, give
         * it back to the modem and send the signal.
         * Otherwise this will happen when we give up
         * the mmio hw sem in modem_release_mmio().
         */
        if (mc->mmio_signal_bits && !mc->mmio_owner) {
            writel(0, mc->mmio + OFF_SEM);
            writel(MB_VALID | mc->mmio_signal_bits,
                   mc->mmio + OFF_MBOX_AP);
            mc->mmio_signal_bits = 0;
        }
    }
done:
    spin_unlock_irqrestore(&mc->lock, flags);
    return IRQ_HANDLED;
}