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; }
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); }
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; }
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); }
/* 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; } }
/* 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; } }
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; }
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; }
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; }
/* 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; }
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; }