how to initialize the tty subsystem ?
1.chr_dev_init -> tty_init
这个tty_init必须的?什么用途?
fs_initcall(chr_dev_init);chr_dev_init -> tty_init
int __init tty_init(void)
{
cdev_init(&tty_cdev, &tty_fops);
cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1);
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty");
device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
and /dev/console
/*crw-rw-rw- root root 5, 0 2012-02-24 04:30 tty*/
/*crw------- root root 5, 1 2012-02-24 04:32 console*/
}
2.tty是怎样工作的?写个 tty driver 从哪里开始?
2.1 create tty_driver, tty_port_init and tty_register_device
1)create a tty_driver结构体,初始化,调用函数tty_register_driver注册该tty driver;2)tty_port_init(port operatoin);
3)tty_register_device;
2.2 通过设备文件节点,调用注册的file operations
后来的就是有char device文件调用引起的一连串行为
3 怎样写tty driver
从上面看出我们需要做的就是2.1部分,其他的就是系统提供的。下面看一下tty driver是怎样创建的?
3.1.对写驱动而言,首先接触的 tty相关的结构体是tty_driver
struct tty_driver {int magic; /* magic number for this structure */
struct kref kref; /* Reference management */
struct cdev cdev;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
int flags; /* tty driver flags */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */
/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct ktermios **termios;
void *driver_state;
/*
* Driver methods
*/
const struct tty_operations *ops;
struct list_head tty_drivers;
};
3.2 tty_driver 结构体的创建
static int smd_tty_init(struct smd_driver_info *info){
int ret;
int i;
smd_tty_driver = alloc_tty_driver(32);
if (smd_tty_driver == 0)
return -ENOMEM;
smd_tty_driver->owner = THIS_MODULE;
smd_tty_driver->driver_name = "smd_tty_driver";
/*数据成员name,major,minor虽然出现在tty_driver中,却是用于创建char device的?
*为什么不出现在 tty_device 中?没有tty_device这个结构体?
*是个没有tty_device这个结构体,作为输入只能放这里了
*/smd_tty_driver->name = "ttyN";
smd_tty_driver->major = 0;
smd_tty_driver->minor_start = 0;
/*TTY_DRIVER_TYPE_SERIAL有什么用途?
*从代码只有fs/proc/proc_tty.c:show_tty_range ->
* case TTY_DRIVER_TYPE_SERIAL:
* seq_puts(m, "serial");
*除了 proc文件系统的显示输出“serial”没什么高深用途。
*/smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
/*从代码没找到使用SERIAL_TYPE_NORMAL的地方*/smd_tty_driver->subtype = SERIAL_TYPE_NORMAL;
/*ktermios: 串口的控制相关,比如baud rate 等*/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 = B115200 | 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;
for (i = 0; i < SMD_TTY_MAX; i++)
{
tty_port_init(&info->smd_tty[i].port);
info->smd_tty[i].id=i;
info->smd_tty[i].port.ops = &smd_tty_port_ops;
tty_register_device(smd_tty_driver, info->smd_tty[i].id, 0);
}
return 0;
}
3.2.1 有关tty flag
/*
* TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
* termios setting when the last process has closed the device.
* Used for PTY's, in particular.
*
* TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
* guarantee never not to set any special character handling
* flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
* !INPCK)). That is, if there is no reason for the driver to
* send notifications of parity and break characters up to the
* line driver, it won't do so. This allows the line driver to
* optimize for this case if this flag is set. (Note that there
* is also a promise, if the above case is true, not to signal
* overruns, either.)
*
*如果设置该flag,就需要单独调用tty_register_device创建cdev,
*如果不set this flag,就根据tty_driver->num,创建num个cdev。
* TTY_DRIVER_DYNAMIC_DEV --- if set, the individual tty devices need
* to be registered with a call to tty_register_device() when the
* device is found in the system and unregistered with a call to
* tty_unregister_device() so the devices will be show up
* properly in sysfs. If not set, driver->num entries will be
* created by the tty core in sysfs when tty_register_driver() is
* called. This is to be used by drivers that have tty devices
* that can appear and disappear while the main tty driver is
* registered with the tty core.
*/
3.2.2 tty_driver内存分配
static struct tty_driver *smd_tty_driver;
为这个指针变量指向的内容分配内存:smd_tty_driver = alloc_tty_driver(32);
#define alloc_tty_driver(lines) __alloc_tty_driver(lines, THIS_MODULE)
/*__alloc_tty_driver为tty_driver分配内存,并初始化该driver管理的设备个数*/
struct tty_driver *__alloc_tty_driver(int lines, struct module *owner)
{
struct tty_driver *driver;
driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
kref_init(&driver->kref);
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
driver->owner = owner;
/* later we'll move allocation of tables here */
}
return driver;
}
3.2.3 tty_operations
/*tty_operations: 的函数指针很多,可以分为三类:*(1):输入参数包含tty_driver的:lookup,install and remove
*(2):输入参数包含tty_struct的
*(3):const struct file_operations *proc_fops;
*这只多tty_operations赋值以下几个函数指针,其他的都是不用的?
static const 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,
};
check的方法是从crash中看一下这个全局变量,这是个const型,它的内容应该不会被改变
也就是说不会在被赋值,其他函数应该是不用啦。
*/
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct inode *inode, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
const struct file_operations *proc_fops;
};
3.2.4 tty_register_driver
/** Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int i;
dev_t dev;
void **p = NULL;
struct device *d;
/*为指针指向的内容分配memory*/
if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
}
/*动态得到dev_t*/
if (!driver->major) {
error = alloc_chrdev_region(&dev, driver->minor_start,
driver->num, driver->name);
if (!error) {
driver->major = MAJOR(dev);
driver->minor_start = MINOR(dev);
}
}
/*赋值ttys and termios*/
if (p) {
driver->ttys = (struct tty_struct **)p;
driver->termios = (struct ktermios **)(p + driver->num);
}
/*value the cdev,并初始化该设备结点的file operations: tty_fops*/
cdev_init(&driver->cdev, &tty_fops);
driver->cdev.owner = driver->owner;
cdev_add(&driver->cdev, dev, driver->num);
/*把成员tty_drivers的地址连接到list head:tty_drivers中
*tty_drivers这个全局变量具有重要作用,从中可以得到tty_driver
*/
mutex_lock(&tty_mutex);
list_add(&driver->tty_drivers, &tty_drivers);
mutex_unlock(&tty_mutex);
if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
if (IS_ERR(d)) {
error = PTR_ERR(d);
goto err;
}
}
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
}
3.2.5 tty_register_device
/*tty_register_device赋值这 device_create的输入参数,然后调用 device_create*/
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
char name[64];
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name);
else
tty_line_name(driver, index, name);
pr_err("tty-->:tty_register_device: dev node\n");
return device_create(tty_class, device, dev, NULL, name);
}
4. 这里还有一个 tty_port and tty_port_operation,
tty_port在那个层次上?什么时候起作用?/*
* Port level information. Each device keeps its own port level information
* so provide a common structure for those ports wanting to use common support
* routines.
*
* The tty port has a different lifetime to the tty so must be kept apart.
* In addition be careful as tty -> port mappings are valid for the life
* of the tty object but in many cases port -> tty mappings are valid only
* until a hangup so don't use the wrong path.
*/
struct tty_port_operations {
/* Return 1 if the carrier is raised */
int (*carrier_raised)(struct tty_port *port);
/* Control the DTR line */
void (*dtr_rts)(struct tty_port *port, int raise);
/* Called when the last close completes or a hangup finishes
IFF the port was initialized. Do not use to free resources. Called
under the port mutex to serialize against activate/shutdowns */
void (*shutdown)(struct tty_port *port);
void (*drop)(struct tty_port *port);
/* Called under the port mutex from tty_port_open, serialized using
the port mutex */
/* FIXME: long term getting the tty argument *out* of this would be
good for consoles */
int (*activate)(struct tty_port *port, struct tty_struct *tty);
/* Called on the final put of a port */
void (*destruct)(struct tty_port *port);
};
static const struct tty_port_operations smd_tty_port_ops = {
.shutdown = smd_tty_port_shutdown,
.activate = smd_tty_port_activate,
};
总之, 实现tty_driver 就是创建 tty_driver并赋值。主要的工作是写tty_operations的实现,至于 tty_port_operations是不是必须的待查明。