static ssize_t open_timeout_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { unsigned int num_dev; unsigned long wait; if (dev == NULL) { SMD_TTY_INFO("%s: Invalid Device passed", __func__); return -EINVAL; } for (num_dev = 0; num_dev < MAX_SMD_TTYS; num_dev++) { if (dev == smd_tty[num_dev].device_ptr) break; } if (num_dev >= MAX_SMD_TTYS) { SMD_TTY_ERR("[%s]: Device Not found", __func__); return -EINVAL; } if (!kstrtoul(buf, 10, &wait)) { smd_tty[num_dev].open_wait = wait; return n; } else { SMD_TTY_INFO("[%s]: Unable to convert %s to an int", __func__, buf); return -EINVAL; } }
static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len) { struct smd_tty_info *info = tty->driver_data; int avail; /* if we're writing to a packet channel we will ** never be able to write more data than there ** is currently space for */ if (is_in_reset(info)) return -ENETRESET; avail = smd_write_avail(info->ch); /* if no space, we'll have to setup a notification later to wake up the * tty framework when space becomes avaliable */ if (!avail) { smd_enable_read_intr(info->ch); return 0; } if (len > avail) len = avail; SMD_TTY_INFO("[WRITE]: PID %u -> port %s %x bytes", current->pid, info->smd->port_name, len); return smd_write(info->ch, buf, len); }
static ssize_t open_timeout_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned int num_dev; unsigned int open_wait; if (dev == NULL) { SMD_TTY_INFO("%s: Invalid Device passed", __func__); return -EINVAL; } for (num_dev = 0; num_dev < MAX_SMD_TTYS; num_dev++) { if (dev == smd_tty[num_dev].device_ptr) break; } if (num_dev >= MAX_SMD_TTYS) { SMD_TTY_ERR("[%s]: Device Not Found", __func__); return -EINVAL; } mutex_lock(&smd_tty[num_dev].open_lock_lha1); open_wait = smd_tty[num_dev].open_wait; mutex_unlock(&smd_tty[num_dev].open_lock_lha1); return snprintf(buf, PAGE_SIZE, "%d\n", open_wait); }
static void smd_tty_port_shutdown(struct tty_port *tport) { struct smd_tty_info *info; struct tty_struct *tty = tty_port_tty_get(tport); unsigned long flags; #ifdef CONFIG_LGE_USES_SMD_DS_TTY int res = 0; int n = tty->index; #endif info = tty->driver_data; if (info == 0) { tty_kref_put(tty); return; } mutex_lock(&smd_tty_lock); spin_lock_irqsave(&info->reset_lock, flags); info->is_open = 0; spin_unlock_irqrestore(&info->reset_lock, flags); tasklet_kill(&info->tty_tsklt); wake_lock_destroy(&info->wake_lock); wake_lock_destroy(&info->ra_wake_lock); SMD_TTY_INFO("%s with PID %u closed port %s", current->comm, current->pid, info->smd->port_name); tty->driver_data = NULL; del_timer(&info->buf_req_timer); smd_close(info->ch); #ifdef CONFIG_LGE_USES_SMD_DS_TTY /* */ pr_info("%s: waiting to close smd %s completely\n", __func__, smd_tty[n].smd->port_name); /* wait for reopen ready status in seconds */ res = wait_event_interruptible_timeout( info->ch_opened_wait_queue, !info->is_open, (lge_ds_modem_wait * HZ)); if (res == 0) { /* just in case, remain result value */ res = -ETIMEDOUT; pr_err("%s: timeout to wait for %s smd_close.\ next smd_open may fail....%d\n", __func__, smd_tty[n].smd->port_name, res); }
static int smd_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct smd_tty_info *info = tty->driver_data; if (info->in_reset) return -ENETRESET; SMD_TTY_INFO("PID %u --> %s Set: %x Clear: %x", current->pid, __func__, set, clear); return smd_tiocmset(info->ch, set, clear); }
/* * Returns the current TIOCM status bits including: * SMD Signals (DTR/DSR, CTS/RTS, CD, RI) * TIOCM_OUT1 - reset state (1=in reset) * TIOCM_OUT2 - reset state updated (1=updated) */ static int smd_tty_tiocmget(struct tty_struct *tty) { struct smd_tty_info *info = tty->driver_data; unsigned long flags; int tiocm; tiocm = smd_tiocmget(info->ch); spin_lock_irqsave(&info->reset_lock, flags); tiocm |= (info->in_reset ? TIOCM_OUT1 : 0); if (info->in_reset_updated) { tiocm |= TIOCM_OUT2; info->in_reset_updated = 0; } SMD_TTY_INFO("PID %u --> %s TIOCM is %x ", current->pid, __func__, tiocm); spin_unlock_irqrestore(&info->reset_lock, flags); return tiocm; }
static void smd_tty_port_shutdown(struct tty_port *tport) { struct smd_tty_info *info; struct tty_struct *tty = tty_port_tty_get(tport); unsigned long flags; #ifdef CONFIG_MSM_SMD_TTY_DS_LEGACY int res = 0; int n = tty->index; #endif info = tty->driver_data; if (info == 0) { tty_kref_put(tty); return; } mutex_lock(&smd_tty_lock); spin_lock_irqsave(&info->reset_lock, flags); info->is_open = 0; spin_unlock_irqrestore(&info->reset_lock, flags); tasklet_kill(&info->tty_tsklt); wake_lock_destroy(&info->wake_lock); wake_lock_destroy(&info->ra_wake_lock); SMD_TTY_INFO("%s with PID %u closed port %s", current->comm, current->pid, info->smd->port_name); tty->driver_data = NULL; del_timer(&info->buf_req_timer); smd_close(info->ch); #ifdef CONFIG_MSM_SMD_TTY_DS_LEGACY /* * At current smd_tty framework, if smd_tty_open() * is invoked by process before smd_tty_close() is * completely finished, smd_tty_open() may fail * because smd_tty_close() does not wait to close smd * channel from modem. To fix this situation, new SMD * notify status, SMD_EVENT_REOPEN_READY is used. * Until smd_tty receive this status, smd_tty_close() * will be wait(in fact, process will be wait). */ pr_info("%s: waiting to close smd %s completely\n", __func__, smd_tty[n].smd->port_name); /* wait for reopen ready status in seconds */ res = wait_event_interruptible_timeout( info->ch_opened_wait_queue, !info->is_open, (smd_tty_ds_modem_wait * HZ)); if (res <= 0) { /* just in case, remain result value */ pr_err("%s: timeout to wait for %s smd_close. " "next smd_open may fail\n", __func__, smd_tty[n].smd->port_name); } #endif info->ch = NULL; subsystem_put(info->pil); mutex_unlock(&smd_tty_lock); tty_kref_put(tty); }
static int smd_tty_port_activate(struct tty_port *tport, struct tty_struct *tty) { int res = 0; unsigned int n = tty->index; struct smd_tty_info *info; const char *peripheral = NULL; if (n >= MAX_SMD_TTYS || !smd_tty[n].smd) return -ENODEV; info = smd_tty + n; mutex_lock(&smd_tty_lock); tty->driver_data = info; peripheral = smd_edge_to_subsystem(smd_tty[n].smd->edge); if (peripheral) { info->pil = subsystem_get(peripheral); if (IS_ERR(info->pil)) { SMD_TTY_INFO( "%s failed on smd_tty device :%s subsystem_get failed for %s", __func__, smd_tty[n].smd->port_name, peripheral); /* * Sleep, inorder to reduce the frequency of * retry by user-space modules and to avoid * possible watchdog bite. */ msleep((smd_tty[n].open_wait * 1000)); res = PTR_ERR(info->pil); goto out; } /* Wait for the modem SMSM to be inited for the SMD * Loopback channel to be allocated at the modem. Since * the wait need to be done atmost once, using msleep * doesn't degrade the performance. */ if (n == LOOPBACK_IDX) { if (!is_modem_smsm_inited()) msleep(5000); smsm_change_state(SMSM_APPS_STATE, 0, SMSM_SMD_LOOPBACK); msleep(100); } /* * Wait for a channel to be allocated so we know * the modem is ready enough. */ if (smd_tty[n].open_wait) { res = wait_for_completion_interruptible_timeout( &info->ch_allocated, msecs_to_jiffies(smd_tty[n].open_wait * 1000)); if (res == 0) { SMD_TTY_INFO( "Timed out waiting for SMD channel %s", smd_tty[n].smd->port_name); res = -ETIMEDOUT; goto release_pil; } else if (res < 0) { SMD_TTY_INFO( "Error waiting for SMD channel %s : %d\n", smd_tty[n].smd->port_name, res); goto release_pil; } #ifdef CONFIG_MSM_SMD_TTY_DS_LEGACY /* * on boot, process tried to open smd0 sleeps until * modem is ready or timeout. */ if (n == DS_IDX) { /* wait for open ready status in seconds */ pr_info("%s: checking DS modem status\n", __func__); res = wait_event_interruptible_timeout( info->ch_opened_wait_queue, info->is_dsmodem_ready, (smd_tty_ds_modem_wait * HZ)); if (!res) { res = -ETIMEDOUT; pr_err("%s: timeout to wait for %s modem: %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } else if (res < 0) { pr_err("%s: Error waiting for %s modem: %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_pil; } pr_info("%s: DS modem is OK, open smd0..\n", __func__); } #endif } } tasklet_init(&info->tty_tsklt, smd_tty_read, (unsigned long)info); wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, smd_tty[n].smd->port_name); scnprintf(info->ra_wake_lock_name, MAX_RA_WAKE_LOCK_NAME_LEN, "SMD_TTY_%s_RA", smd_tty[n].smd->port_name); wake_lock_init(&info->ra_wake_lock, WAKE_LOCK_SUSPEND, info->ra_wake_lock_name); res = smd_named_open_on_edge(smd_tty[n].smd->port_name, smd_tty[n].smd->edge, &info->ch, info, smd_tty_notify); if (res < 0) { SMD_TTY_INFO("%s: %s open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto release_wl_tl; } res = wait_event_interruptible_timeout(info->ch_opened_wait_queue, info->is_open, (2 * HZ)); if (res == 0) res = -ETIMEDOUT; if (res < 0) { SMD_TTY_INFO("%s: wait for %s smd_open failed %d\n", __func__, smd_tty[n].smd->port_name, res); goto close_ch; } SMD_TTY_INFO("%s with PID %u opened port %s", current->comm, current->pid, smd_tty[n].smd->port_name); smd_disable_read_intr(info->ch); mutex_unlock(&smd_tty_lock); return 0; close_ch: smd_close(info->ch); info->ch = NULL; release_wl_tl: tasklet_kill(&info->tty_tsklt); wake_lock_destroy(&info->wake_lock); wake_lock_destroy(&info->ra_wake_lock); release_pil: subsystem_put(info->pil); out: mutex_unlock(&smd_tty_lock); return res; }