您的位置:首页 > 运维架构 > Linux

Linux中终端设备驱动

2012-05-04 11:06 363 查看
核心层

1、在tty_io.c文件(drivers/char/ )

static const struct file_operations tty_fops = {
.llseek		= no_llseek,
.read		= tty_read,
.write		= tty_write,
.poll		= tty_poll,
.unlocked_ioctl	= tty_ioctl,
.compat_ioctl	= tty_compat_ioctl,
.open		= tty_open,
.release	= tty_release,
.fasync		= tty_fasync,
};


static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct tty_struct *tty;
struct inode *inode = file->f_path.dentry->d_inode;
ssize_t ret;
struct tty_ldisc *ld;

tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}


在tty_write()函数中,调用了线路规程:

其中在 tty_struct 结构中,有成员 tty_ldsic:

struct tty_struct {
int	magic;
struct kref kref;
struct tty_driver *driver;
const struct tty_operations *ops;
int index;
/* The ldisc objects are protected by tty_ldisc_lock at the moment */
struct tty_ldisc ldisc;
struct mutex termios_mutex;
spinlock_t ctrl_lock;
/* Termios values are protected by the termios mutex */
struct ktermios *termios, *termios_locked;
struct termiox *termiox;	/* May be NULL for unsupported */
char name[64];
struct pid *pgrp;		/* Protected by ctrl lock */
struct pid *session;
unsigned long flags;
int count;
struct winsize winsize;		/* termios mutex */
unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
unsigned char low_latency:1, warned:1;
unsigned char ctrl_status;	/* ctrl_lock */
unsigned int receive_room;	/* Bytes free for queue */

struct tty_struct *link;
struct fasync_struct *fasync;
struct tty_bufhead buf;		/* Locked internally */
int alt_speed;		/* For magic substitution of 38400 bps */
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096

/*
* The following is data for the N_TTY line discipline.  For
* historical reasons, this is included in the tty structure.
* Mostly locked by the BKL.
*/
unsigned int column;
unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
unsigned char closing:1;
unsigned char echo_overrun:1;
unsigned short minimum_to_wake;
unsigned long overrun_time;
int num_overrun;
unsigned long process_char_map[256/(8*sizeof(unsigned long))];
char *read_buf;
int read_head;
int read_tail;
int read_cnt;
unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
unsigned char *echo_buf;
unsigned int echo_pos;
unsigned int echo_cnt;
int canon_data;
unsigned long canon_head;
unsigned int canon_column;
struct mutex atomic_read_lock;
struct mutex atomic_write_lock;
struct mutex output_lock;
struct mutex echo_lock;
unsigned char *write_buf;
int write_cnt;
spinlock_t read_lock;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
};


在源码中,搜索 “tty_ldisc_ops” ,就可以,找到所有的 线路规程; 典型的 /drviers/char/n_tty.c文件针对 N_TTY线路规程实现了 tty_disc结构体中的成员。

struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic           = TTY_LDISC_MAGIC,
.name            = "n_tty",
.open            = n_tty_open,
.close           = n_tty_close,
.flush_buffer    = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read            = n_tty_read,
.write           = n_tty_write,
.ioctl           = n_tty_ioctl,
.set_termios     = n_tty_set_termios,
.poll            = n_tty_poll,
.receive_buf     = n_tty_receive_buf,
.write_wakeup    = n_tty_write_wakeup
};


其中的 write 方法实现如下:

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;

/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
retval = tty_check_change(tty);
if (retval)
return retval;
}

/* Write out any echoed characters that are still pending */
process_echoes(tty);

add_wait_queue(&tty->write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
c = *b;
if (process_output(c, tty) < 0)
break;
b++; nr--;
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);  //刷新缓冲区并丢弃任何剩下的数据。
} else {
while (nr > 0) {
c = tty->ops->write(tty, b, nr); //跳转到tty 驱动着一层。
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}


通过上面的调用流程后,就通过指针,线路规程这一层,调用了tty驱动着一层。

尽管一个特定的UAR设备驱动完全可以按照 前面的方法就行设计, 即 定义tty_driver并实现 tty_operation 其中的成员函数,但是linux已经在一个文件 serial_core.c 中实现了 UART 设备的通用 tyy驱动层(姑且称为串口驱动层)。这样子,UART驱动的主要任务就 演变成 实现 serial-core.c中定义的一组 uart_xxx皆苦并非 tty_xxx接口。

1、在serial_core.h中 ,定义了 uart_driver结构

struct uart_driver {
struct module		*owner;
const char		*driver_name;
const char		*dev_name;
int			 major;
int			 minor;
int			 nr;
struct console		*cons;

/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state	*state;
struct tty_driver	*tty_driver;
};


而注册函数演变成 uart_register_driver( ). ,在注册uart driver 的函数 ,实际上也调用了
tty_register_driver ();

int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal = NULL;
int i, retval;

BUG_ON(drv->state);

/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
retval = -ENOMEM;
if (!drv->state)
goto out;

normal  = alloc_tty_driver(drv->nr);
if (!normal)
goto out;

drv->tty_driver = normal;

normal->owner		= drv->owner;
normal->driver_name	= drv->driver_name;
normal->name		= drv->dev_name;
normal->major		= drv->major;
normal->minor_start	= drv->minor;
normal->type		= TTY_DRIVER_TYPE_SERIAL;
normal->subtype		= SERIAL_TYPE_NORMAL;
normal->init_termios	= tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state    = drv;
tty_set_operations(normal, &uart_ops);

/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;

state->close_delay     = 500;	/* .5 seconds */
state->closing_wait    = 30000;	/* 30 seconds */
mutex_init(&state->mutex);

tty_port_init(&state->info.port);
init_waitqueue_head(&state->info.delta_msr_wait);
tasklet_init(&state->info.tlet, uart_tasklet_action,
(unsigned long)state);
}

retval = tty_register_driver(normal);
out:
if (retval < 0) {
put_tty_driver(normal);
kfree(drv->state);
}
return retval;
}

uart_port 结构如下 :

struct uart_port {
spinlock_t		lock;			/* port lock */
unsigned long		iobase;			/* in/out[bwl] */
unsigned char __iomem	*membase;		/* read/write[bwl] */
unsigned int		(*serial_in)(struct uart_port *, int);
void			(*serial_out)(struct uart_port *, int, int);
unsigned int		irq;			/* irq number */
unsigned int		uartclk;		/* base uart clock */
unsigned int		fifosize;		/* tx fifo size */
unsigned char		x_char;			/* xon/xoff char */
unsigned char		regshift;		/* reg offset shift */
unsigned char		iotype;			/* io access style */
unsigned char		unused1;

#define UPIO_PORT		(0)
#define UPIO_HUB6		(1)
#define UPIO_MEM		(2)
#define UPIO_MEM32		(3)
#define UPIO_AU			(4)			/* Au1x00 type IO */
#define UPIO_TSI		(5)			/* Tsi108/109 type IO */
#define UPIO_DWAPB		(6)			/* DesignWare APB UART */
#define UPIO_RM9000		(7)			/* RM9000 type IO */

unsigned int		read_status_mask;	/* driver specific */
unsigned int		ignore_status_mask;	/* driver specific */
struct uart_info	*info;			/* pointer to parent info */
struct uart_icount	icount;			/* statistics */

struct console		*cons;			/* struct console, if any */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long		sysrq;			/* sysrq timeout */
#endif

upf_t			flags;

#define UPF_FOURPORT		((__force upf_t) (1 << 1))
#define UPF_SAK			((__force upf_t) (1 << 2))
#define UPF_SPD_MASK		((__force upf_t) (0x1030))
#define UPF_SPD_HI		((__force upf_t) (0x0010))
#define UPF_SPD_VHI		((__force upf_t) (0x0020))
#define UPF_SPD_CUST		((__force upf_t) (0x0030))
#define UPF_SPD_SHI		((__force upf_t) (0x1000))
#define UPF_SPD_WARP		((__force upf_t) (0x1010))
#define UPF_SKIP_TEST		((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ		((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD		((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY		((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART		((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST	((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER	((__force upf_t) (1 << 16))
#define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE		((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF	((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT		((__force upf_t) (1 << 29))
#define UPF_DEAD		((__force upf_t) (1 << 30))
#define UPF_IOREMAP		((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK		((__force upf_t) (0x17fff))
#define UPF_USR_MASK		((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

unsigned int		mctrl;			/* current modem ctrl settings */
unsigned int		timeout;		/* character-based timeout */
unsigned int		type;			/* port type */
const struct uart_ops	*ops;
unsigned int		custom_divisor;
unsigned int		line;			/* port index */
resource_size_t		mapbase;		/* for ioremap */
struct device		*dev;			/* parent device */
unsigned char		hub6;			/* this should be in the 8250 driver */
unsigned char		suspended;
unsigned char		unused[2];
void			*private_data;		/* generic platform data pointer */
};


在serial_core.c 中,提供了下面一个函数来 添加一个端口: uart_add_one_port()函数。在uart_register_dirver之后,uart_add_one_port 一个重要作用就是 封装了 tty_register_device( );

int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
{
struct uart_state *state;
int ret = 0;
struct device *tty_dev;

BUG_ON(in_interrupt());

if (port->line >= drv->nr)
return -EINVAL;

state = drv->state + port->line;

mutex_lock(&port_mutex);
mutex_lock(&state->mutex);
if (state->port) {
ret = -EINVAL;
goto out;
}

state->port = port;
state->pm_state = -1;

port->cons = drv->cons;
port->info = &state->info;

/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) {
spin_lock_init(&port->lock);
lockdep_set_class(&port->lock, &port_lock_key);
}

uart_configure_port(drv, state, port);

/*
* Register the port whether it's detected or not.  This allows
* setserial to be used to alter this ports parameters.
*/
tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev);
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1);
device_set_wakeup_enable(tty_dev, 0);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
port->line);

/*
* Ensure UPF_DEAD is not set.
*/
port->flags &= ~UPF_DEAD;

out:
mutex_unlock(&state->mutex);
mutex_unlock(&port_mutex);

return ret;
}


uart_ops定义了一系列的操作,如果说,tty_operation对于串口还比较抽象的话,那么 uart_ops则直接面向 了 串口的UART 。这非常类似于面向对象中的 基类和派生类的关系。

struct uart_ops {
unsigned int	(*tx_empty)(struct uart_port *);
void		(*set_mctrl)(struct uart_port *, unsigned int mctrl);
unsigned int	(*get_mctrl)(struct uart_port *);
void		(*stop_tx)(struct uart_port *);
void		(*start_tx)(struct uart_port *);
void		(*send_xchar)(struct uart_port *, char ch);
void		(*stop_rx)(struct uart_port *);
void		(*enable_ms)(struct uart_port *);
void		(*break_ctl)(struct uart_port *, int ctl);
int		(*startup)(struct uart_port *);
void		(*shutdown)(struct uart_port *);
void		(*flush_buffer)(struct uart_port *);
void		(*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void		(*set_ldisc)(struct uart_port *);
void		(*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
int		(*set_wake)(struct uart_port *, unsigned int state);
void		(*wake_peer)(struct uart_port *);

/*
* Return a string describing the type of the port
*/
const char *(*type)(struct uart_port *);

/*
* Release IO and memory resources used by the port.
* This includes iounmap if necessary.
*/
void		(*release_port)(struct uart_port *);

/*
* Request IO and memory resources used by the port.
* This includes iomapping the port if necessary.
*/
int		(*request_port)(struct uart_port *);
void		(*config_port)(struct uart_port *, int);
int		(*verify_port)(struct uart_port *, struct serial_struct *);
int		(*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
void	(*poll_put_char)(struct uart_port *, unsigned char);
int		(*poll_get_char)(struct uart_port *);
#endif
};


在 serial_console.c 文件中 定义了 tty_operation的事例,

static const struct tty_operations uart_ops = {
.open		= uart_open,
.close		= uart_close,
.write		= uart_write,
.put_char	= uart_put_char,
.flush_chars	= uart_flush_chars,
.write_room	= uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer	= uart_flush_buffer,
.ioctl		= uart_ioctl,
.throttle	= uart_throttle,
.unthrottle	= uart_unthrottle,
.send_xchar	= uart_send_xchar,
.set_termios	= uart_set_termios,
.set_ldisc	= uart_set_ldisc,
.stop		= uart_stop,
.start		= uart_start,
.hangup		= uart_hangup,
.break_ctl	= uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.read_proc	= uart_read_proc,
#endif
.tiocmget	= uart_tiocmget,
.tiocmset	= uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
.poll_init	= uart_poll_init,
.poll_get_char	= uart_poll_get_char,
.poll_put_char	= uart_poll_put_char,
#endif
};


这些函数会借助 uar_ops结构体中的成员来完成具体的操作。

比如::tty_operations的 uart_send_xchar()成员函数利用了 uart_ops结构体的star_tx来进行初始化。

static void uart_send_xchar(struct tty_struct *tty, char ch)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
unsigned long flags;

if (port->ops->send_xchar)
port->ops->send_xchar(port, ch);
else {
port->x_char = ch;
if (ch) {
spin_lock_irqsave(&port->lock, flags);
port->ops->start_tx(port);
spin_unlock_irqrestore(&port->lock, flags);
}
}
}


在使用了串口核心层tty接口后,一个串口的要完成的主要工作是

1\ 定义uart_diver  uart_ops uart_port

2\ uart_register_diver() uart_add_one_port,注册驱动并添加端口。

3\ 根据具体的datasheet 实现 uart_ops中的成员。这些函数成了实现UART驱动的主体工作。

下面根据这三部来分析 S3C6410的串口驱动:

1\ S3C6410具有4路高速串口。具体见开发板硬件手册。

在samsung.c文件,在官方下的内核改文件已经添加了进来。

static int __init s3c24xx_serial_modinit(void)
{
int ret;

ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
printk(KERN_ERR "failed to register UART driver\n");
return -1;
}

return 0;
}


但是 在同一目录下的 S3C6400。.C 文件并不存在。

static int __init s3c6400_serial_init(void)
{
return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf);
}


-->

static struct platform_driver s3c6400_serial_drv = {
.probe		= s3c6400_serial_probe,
.remove		= s3c24xx_serial_remove,
.driver		= {
.name	= "s3c6400-uart",
.owner	= THIS_MODULE,
},
}


static int s3c6400_serial_probe(struct platform_device *dev)
{
dbg("s3c6400_serial_probe: dev=%p\n", dev);
return s3c24xx_serial_probe(dev, &s3c6400_uart_inf);
}


int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
struct s3c24xx_uart_port *ourport;
int ret;

dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);

ourport = &s3c24xx_serial_ports[probe_index];
probe_index++;

dbg("%s: initialising port %p...\n", __func__, ourport);

ret = s3c24xx_serial_init_port(ourport, info, dev);
if (ret < 0)
goto probe_err;

dbg("%s: adding port\n", __func__);
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
platform_set_drvdata(dev, &ourport->port);

ret = device_create_file(&dev->dev, &dev_attr_clock_source);
if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);

ret = s3c24xx_serial_cpufreq_register(ourport);
if (ret < 0)
dev_err(&dev->dev, "failed to add cpufreq notifier\n");

return 0;

probe_err:
return ret;
}


被添加的 uart_port uart_ops成员 定义在 samsung.c文件中

static struct uart_ops s3c24xx_serial_ops = {
.pm		= s3c24xx_serial_pm,
.tx_empty	= s3c24xx_serial_tx_empty,
.get_mctrl	= s3c24xx_serial_get_mctrl,
.set_mctrl	= s3c24xx_serial_set_mctrl,
.stop_tx	= s3c24xx_serial_stop_tx,
.start_tx	= s3c24xx_serial_start_tx,
.stop_rx	= s3c24xx_serial_stop_rx,
.enable_ms	= s3c24xx_serial_enable_ms,
.break_ctl	= s3c24xx_serial_break_ctl,
.startup	= s3c24xx_serial_startup,
.shutdown	= s3c24xx_serial_shutdown,
.set_termios	= s3c24xx_serial_set_termios,
.type		= s3c24xx_serial_type,
.release_port	= s3c24xx_serial_release_port,
.request_port	= s3c24xx_serial_request_port,
.config_port	= s3c24xx_serial_config_port,
.verify_port	= s3c24xx_serial_verify_port,
};


从源代码中发现:

tatic struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX0,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 0,
}
},
[1] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX1,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

[2] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX2,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 2,
}
},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
[3] = {
.port = {
.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
.iotype		= UPIO_MEM,
.irq		= IRQ_S3CUART_RX3,
.uartclk	= 0,
.fifosize	= 16,
.ops		= &s3c24xx_serial_ops,
.flags		= UPF_BOOT_AUTOCONF,
.line		= 3,
}
}
#endif
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: