kernel-ark/drivers/staging/dream/smd/smd_tty.c
Brian Swetland 0d8dc6b05b Staging: HTC Dream: add smd code
Infrastructure to support the Qualcomm "shared memory driver"
interface, used to communicate with the baseband processor on MSM7k
SoCs.  The smd core provides low level facilities to interact with the
shared memory comms region, and a "virtual serial channel" interface
that higher level transports (AT command channel, rmnet virtual
ethernet, qmi network management protocol, and oncrpc, for example)
are routed over.

Signed-off-by: Pavel Machek <pavel@ucw.cz>
Cc: Brian Swetland <swetland@google.com>
Cc: Iliyan Malchev <ibm@android.com>
Cc: San Mehat <san@android.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2009-09-15 12:01:42 -07:00

214 lines
4.9 KiB
C

/* arch/arm/mach-msm/smd_tty.c
*
* Copyright (C) 2007 Google, Inc.
* Author: Brian Swetland <swetland@google.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/wakelock.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <mach/msm_smd.h>
#define MAX_SMD_TTYS 32
static DEFINE_MUTEX(smd_tty_lock);
struct smd_tty_info {
smd_channel_t *ch;
struct tty_struct *tty;
struct wake_lock wake_lock;
int open_count;
};
static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
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);
}
static int smd_tty_open(struct tty_struct *tty, struct file *f)
{
int res = 0;
int n = tty->index;
struct smd_tty_info *info;
const char *name;
if (n == 0) {
name = "SMD_DS";
} else if (n == 27) {
name = "SMD_GPSNMEA";
} else {
return -ENODEV;
}
info = smd_tty + n;
mutex_lock(&smd_tty_lock);
wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND, name);
tty->driver_data = info;
if (info->open_count++ == 0) {
info->tty = tty;
if (info->ch) {
smd_kick(info->ch);
} else {
res = smd_open(name, &info->ch, info, smd_tty_notify);
}
}
mutex_unlock(&smd_tty_lock);
return res;
}
static void smd_tty_close(struct tty_struct *tty, struct file *f)
{
struct smd_tty_info *info = tty->driver_data;
if (info == 0)
return;
mutex_lock(&smd_tty_lock);
if (--info->open_count == 0) {
info->tty = 0;
tty->driver_data = 0;
wake_lock_destroy(&info->wake_lock);
if (info->ch) {
smd_close(info->ch);
info->ch = 0;
}
}
mutex_unlock(&smd_tty_lock);
}
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
*/
avail = smd_write_avail(info->ch);
if (len > avail)
len = avail;
return smd_write(info->ch, buf, len);
}
static int smd_tty_write_room(struct tty_struct *tty)
{
struct smd_tty_info *info = tty->driver_data;
return smd_write_avail(info->ch);
}
static int smd_tty_chars_in_buffer(struct tty_struct *tty)
{
struct smd_tty_info *info = tty->driver_data;
return smd_read_avail(info->ch);
}
static void smd_tty_unthrottle(struct tty_struct *tty)
{
struct smd_tty_info *info = tty->driver_data;
smd_kick(info->ch);
}
static struct tty_operations smd_tty_ops = {
.open = smd_tty_open,
.close = smd_tty_close,
.write = smd_tty_write,
.write_room = smd_tty_write_room,
.chars_in_buffer = smd_tty_chars_in_buffer,
.unthrottle = smd_tty_unthrottle,
};
static struct tty_driver *smd_tty_driver;
static int __init smd_tty_init(void)
{
int ret;
smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
if (smd_tty_driver == 0)
return -ENOMEM;
smd_tty_driver->owner = THIS_MODULE;
smd_tty_driver->driver_name = "smd_tty_driver";
smd_tty_driver->name = "smd";
smd_tty_driver->major = 0;
smd_tty_driver->minor_start = 0;
smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
smd_tty_driver->init_termios = tty_std_termios;
smd_tty_driver->init_termios.c_iflag = 0;
smd_tty_driver->init_termios.c_oflag = 0;
smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
smd_tty_driver->init_termios.c_lflag = 0;
smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS |
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
tty_set_operations(smd_tty_driver, &smd_tty_ops);
ret = tty_register_driver(smd_tty_driver);
if (ret) return ret;
/* this should be dynamic */
tty_register_device(smd_tty_driver, 0, 0);
tty_register_device(smd_tty_driver, 27, 0);
return 0;
}
module_init(smd_tty_init);