Linux UART Console Driver(1)--硬件设备的注册和UART Driver的注册

Linux UART Console Driver(1)–硬件设备的注册和UART Driver的注册

uart console 是嵌入式设备中的console,是嵌入式设备开发调试中不可缺少的部件。uart console 在系统中的设备名是/dev/console.
linux 提供的uart driver框架位于linux/drivers/tty/serial/8250目录当中。uart console driver和tty driver 是紧密相关的(linux 3.10 kernel)。
相关的文件:

  1. 8250 serial相关:8250_core.c 8250_dma.c 8250_early.c
  2. tty 相关: n_tty.c pty.c tty_buffer.c tty_io.c tty_ioctl.c tty_ldisc.c tty_mutex.c tty_port.c
  3. console 相关:/kernel/printk.c

结构框图

这里写图片描述
图 1 uart console driver系统框图
上图为uart console的系统框架图。uart console的工作流程开始前需要建立系统框架,这包括,uart 硬件设备的注册,串口driver的注册,console信息的初始化,tty driver的注册。接下来就可以对console设备进行读写,读console和写console的执行流程是uart console driver工作的关键过程。

uart 硬件设备的注册

关键数据结构和代码片段:

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);
	void			(*set_termios)(struct uart_port *,
				               struct ktermios *new,
				               struct ktermios *old);
	int			(*handle_irq)(struct uart_port *);
	void			(*pm)(struct uart_port *, unsigned int state,
				      unsigned int old);
	void			(*handle_break)(struct uart_port *);
	unsigned int		irq;			/* irq number */
	unsigned long		irqflags;		/* irq flags  */
	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 and RT288x type IO */
#define UPIO_TSI		(5)			/* Tsi108/109 type IO */

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

	struct console		*cons;			/* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
	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))
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
#define UPF_HARD_FLOW		((__force upf_t) (1 << 21))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW		((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW		((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ		((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR		((__force upf_t) (1 << 25))
#define UPF_BUG_THRE		((__force upf_t) (1 << 26))
/* 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		irq_wake;
	unsigned char		unused[2];
	void			*private_data;		/* generic platform data pointer */
};


----------
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		(*throttle)(struct uart_port *);
	void		(*unthrottle)(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 *, int new);
	void		(*pm)(struct uart_port *, unsigned int state,
			      unsigned int oldstate);
	int		(*set_wake)(struct uart_port *, unsigned int state);

	/*
	 * 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
	int		(*poll_init)(struct uart_port *);
	void		(*poll_put_char)(struct uart_port *, unsigned char);
	int		(*poll_get_char)(struct uart_port *);
#endif
};
----------
struct uart_8250_port {
	struct uart_port	port;
	struct timer_list	timer;		/* "no irq" timer */
	struct list_head	list;		/* ports on this IRQ */
	unsigned short		capabilities;	/* port capabilities */
	unsigned short		bugs;		/* port bugs */
	unsigned int		tx_loadsz;	/* transmit fifo load size */
	unsigned char		acr;
	unsigned char		ier;
	unsigned char		lcr;
	unsigned char		mcr;
	unsigned char		mcr_mask;	/* mask of user bits */
	unsigned char		mcr_force;	/* mask of forced bits */
	unsigned char		cur_iotype;	/* Running I/O type */

	/*
	 * Some bits in registers are cleared on a read, so they must
	 * be saved whenever the register is read but the bits will not
	 * be immediately processed.
	 */
#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
	unsigned char		lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
	unsigned char		msr_saved_flags;

	struct uart_8250_dma	*dma;

	/* 8250 specific callbacks */
	int			(*dl_read)(struct uart_8250_port *);
	void			(*dl_write)(struct uart_8250_port *, int);
};


----------


----------
struct uart_8250_dma {
	dma_filter_fn		fn;
	void			*rx_param;
	void			*tx_param;

	int			rx_chan_id;
	int			tx_chan_id;

	struct dma_slave_config	rxconf;
	struct dma_slave_config	txconf;

	struct dma_chan		*rxchan;
	struct dma_chan		*txchan;

	dma_addr_t		rx_addr;
	dma_addr_t		tx_addr;

	dma_cookie_t		rx_cookie;
	dma_cookie_t		tx_cookie;

	void			*rx_buf;

	size_t			rx_size;
	size_t			tx_size;

	unsigned char		tx_running:1;
};

在8250_core.c 中的全局数组

static struct uart_8250_port serial8250_ports[UART_NR];
//UART_NR = 3 在系统框图中可以找到这个数组

设备注册: 在相应arch的serial.c文件中,这里属于bsp的代码。

int __init bsp_serial_port0_init(void)
{
	struct uart_port up;

	/* clear memory */
	memset(&up, 0, sizeof(up));

	/*
	 * UART0
	 */
	up.line = 0;
	up.type = PORT_16550A;
	up.uartclk = BSP_UART0_FREQ;
	up.fifosize = 32;
	up.irq = BSP_IRQ_OTHERS;
	up.flags = UPF_SKIP_TEST|UPF_SHARE_IRQ;
	up.mapbase = BSP_UART0_PADDR;
	up.membase = ioremap_nocache(up.mapbase, BSP_UART0_PSIZE);
	up.regshift = 2;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
	up.iotype = UPIO_MEM32;
	up.serial_out = dwapb_serial_out;
	up.serial_in = dwapb_serial_in;
	up.handle_irq = dwapb_serial_irq;
#else
	up.iotype = UPIO_DWAPB;
	up.private_data = (void *)BSP_UART0_USR;
#endif

	if (early_serial_setup(&up) != 0) {
		panic("Sheipa: taroko subsystem bsp_serial_init failed!");
	}

	return 0;
}

device_initcall(bsp_serial_port0_init);
//port 的注册是有三组的,因为完全一样的,这里只写一组。

----------
int __init early_serial_setup(struct uart_port *port)
{
	struct uart_port *p;

	if (port->line >= ARRAY_SIZE(serial8250_ports))
		return -ENODEV;

	serial8250_isa_init_ports();
	p = &serial8250_ports[port->line].port;
	p->iobase       = port->iobase;
	p->membase      = port->membase;
	p->irq          = port->irq;
	p->irqflags     = port->irqflags;
	p->uartclk      = port->uartclk;
	p->fifosize     = port->fifosize;
	p->regshift     = port->regshift;
	p->iotype       = port->iotype;
	p->flags        = port->flags;
	p->mapbase      = port->mapbase;
	p->private_data = port->private_data;
	p->type		= port->type;
	p->line		= port->line;

	set_io_from_upio(p);
	if (port->serial_in)
		p->serial_in = port->serial_in;
	if (port->serial_out)
		p->serial_out = port->serial_out;
	if (port->handle_irq)
		p->handle_irq = port->handle_irq;
	else
		p->handle_irq = serial8250_default_handle_irq;

	return 0;
}

static void __init serial8250_isa_init_ports(void)//初始化port数组
{
	struct uart_8250_port *up;
	static int first = 1;
	int i, irqflag = 0;

	if (!first)
		return;
	first = 0;

	if (nr_uarts > UART_NR)
		nr_uarts = UART_NR;

	for (i = 0; i < nr_uarts; i++) {
		struct uart_8250_port *up = &serial8250_ports[i];
		struct uart_port *port = &up->port;

		port->line = i;
		spin_lock_init(&port->lock);

		init_timer(&up->timer);
		up->timer.function = serial8250_timeout;
		if (!(up->port.flags & UPF_SKIP_TEST))
			up->cur_iotype = 0xFF;

		/*
		 * ALPHA_KLUDGE_MCR needs to be killed.
		 */
		up->mcr_mask = ~ALPHA_KLUDGE_MCR;
		up->mcr_force = ALPHA_KLUDGE_MCR;

		port->ops = &serial8250_pops;//
	}

	if (share_irqs)
		irqflag = IRQF_SHARED;

	for (i = 0, up = serial8250_ports;
	     i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
	     i++, up++) {//这里不会执行old_serial_port数组的长度为0
		struct uart_port *port = &up->port;

		printk(KERN_INFO"serial board init\n");

		port->iobase   = old_serial_port[i].port;
		port->irq      = irq_canonicalize(old_serial_port[i].irq);
		port->irqflags = old_serial_port[i].irqflags;
		port->uartclk  = old_serial_port[i].baud_base * 16;
		port->flags    = old_serial_port[i].flags;
		port->hub6     = old_serial_port[i].hub6;
		port->membase  = old_serial_port[i].iomem_base;
		port->iotype   = old_serial_port[i].io_type;
		port->regshift = old_serial_port[i].iomem_reg_shift;
		set_io_from_upio(port);
		port->irqflags |= irqflag;
		if (serial8250_isa_config != NULL)
			serial8250_isa_config(i, &up->port, &up->capabilities);

	}
}

8250_core.c 中的struct uart_ops结构:

static struct uart_ops serial8250_pops = {
	.tx_empty	= serial8250_tx_empty,
	.set_mctrl	= serial8250_set_mctrl,
	.get_mctrl	= serial8250_get_mctrl,
	.stop_tx	= serial8250_stop_tx,
	.start_tx	= serial8250_start_tx,
	.stop_rx	= serial8250_stop_rx,
	.enable_ms	= serial8250_enable_ms,
	.break_ctl	= serial8250_break_ctl,
	.startup	= serial8250_startup,
	.shutdown	= serial8250_shutdown,
	.set_termios	= serial8250_set_termios,
	.set_ldisc	= serial8250_set_ldisc,
	.pm		= serial8250_pm,
	.type		= serial8250_type,
	.release_port	= serial8250_release_port,
	.request_port	= serial8250_request_port,
	.config_port	= serial8250_config_port,
	.verify_port	= serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
	.poll_get_char = serial8250_get_poll_char,
	.poll_put_char = serial8250_put_poll_char,
#endif
};

总结下上面的操作:

  1. 用bsp的硬件信息和操作函数填充uart_port.
  2. 用填充过硬件信息的uart_port 初始化serial8250_ports全局数组。

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;
};

struct uart_state {
	struct tty_port		port;

	enum uart_pm_state	pm_state;
	struct circ_buf		xmit;

	struct uart_port	*uart_port;
};
struct tty_driver {
	int	magic;		/* magic number for this structure */
	struct kref kref;	/* Reference management */
	struct cdev *cdevs;
	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 */
	unsigned int	num;	/* number of devices allocated */
	short	type;		/* type of tty driver */
	short	subtype;	/* subtype of tty driver */
	struct ktermios init_termios; /* Initial termios */
	unsigned long	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 tty_port **ports;
	struct ktermios **termios;
	void *driver_state;

	/*
	 * Driver methods
	 */

	const struct tty_operations *ops;
	struct list_head tty_drivers;
};


8250_core.c 文件中

static int __init serial8250_init(void)
{
	int ret;

	serial8250_isa_init_ports();//初始化serial8250_ports数组,此函数在注册uart硬件信息时执行一次,或者在这里执行一次。

	printk(KERN_INFO "Serial: 8250/16550 driver, "
		"%d ports, IRQ sharing %sabled\n", nr_uarts,
		share_irqs ? "en" : "dis");

	serial8250_reg.nr = UART_NR;
	ret = uart_register_driver(&serial8250_reg);//注册serial8250_reg uart_driver
	if (ret)
		goto out;

	serial8250_isa_devs = platform_device_alloc("serial8250",
						    PLAT8250_DEV_LEGACY);//alloc 一个platform device
	if (!serial8250_isa_devs) {
		ret = -ENOMEM;
		goto unreg_pnp;
	}

	ret = platform_device_add(serial8250_isa_devs);
	if (ret)
		goto put_dev;

	serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);

	ret = platform_driver_register(&serial8250_isa_driver);
	if (ret == 0)
		goto out;

	platform_device_del(serial8250_isa_devs);
put_dev:
	platform_device_put(serial8250_isa_devs);
unreg_uart_drv:
	uart_unregister_driver(&serial8250_reg);
out:
	return ret;
}

上面代码中注册的uart_driver:
static struct uart_driver serial8250_reg = {
	.owner			= THIS_MODULE,
	.driver_name		= "serial",
	.dev_name		= "ttyS",
	.major			= TTY_MAJOR,
	.minor			= 64,
	.cons			= SERIAL8250_CONSOLE,
};

注册uart_driver部分:

struct tty_driver {
	int	magic;		/* magic number for this structure */
	struct kref kref;	/* Reference management */
	struct cdev *cdevs;
	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 */
	unsigned int	num;	/* number of devices allocated */
	short	type;		/* type of tty driver */
	short	subtype;	/* subtype of tty driver */
	struct ktermios init_termios; /* Initial termios */
	unsigned long	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 tty_port **ports;
	struct ktermios **termios;
	void *driver_state;

	/*
	 * Driver methods
	 */

	const struct tty_operations *ops;
	struct list_head tty_drivers; //注册时 加入到tty_drivers全局链表中
};

struct tty_port {
	struct tty_bufhead	buf;		/* Locked internally */
	struct tty_struct	*tty;		/* Back pointer */
	struct tty_struct	*itty;		/* internal back ptr */
	const struct tty_port_operations *ops;	/* Port operations */
	spinlock_t		lock;		/* Lock protecting tty field */
	int			blocked_open;	/* Waiting to open */
	int			count;		/* Usage count */
	wait_queue_head_t	open_wait;	/* Open waiters */
	wait_queue_head_t	close_wait;	/* Close waiters */
	wait_queue_head_t	delta_msr_wait;	/* Modem status change */
	unsigned long		flags;		/* TTY flags ASY_*/
	unsigned long		iflags;		/* TTYP_ internal flags */
#define TTYP_FLUSHING			1  /* Flushing to ldisc in progress */
#define TTYP_FLUSHPENDING		2  /* Queued buffer flush pending */
	unsigned char		console:1,	/* port is a console */
				low_latency:1;	/* direct buffer flush */
	struct mutex		mutex;		/* Locking */
	struct mutex		buf_mutex;	/* Buffer alloc lock */
	unsigned char		*xmit_buf;	/* Optional buffer */
	unsigned int		close_delay;	/* Close port delay */
	unsigned int		closing_wait;	/* Delay for output */
	int			drain_delay;	/* Set to zero if no pure time
						   based drain is needed else
						   set to size of fifo */
	struct kref		kref;		/* Ref counter */
};


int uart_register_driver(struct uart_driver *drv)
{
	struct tty_driver *normal;
	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);//创建struct uart_state 数组, 结合框架图, driver的state指向struct uart_state 数组。
	if (!drv->state)
		goto out;

	normal = alloc_tty_driver(drv->nr);//分配tty_driver
	if (!normal)
		goto out_kfree;

	drv->tty_driver = normal;
//初始化tty_driver的相关信息
	normal->driver_name	= drv->driver_name;
	normal->name		= drv->dev_name;
	normal->major		= drv->major;//4
	normal->minor_start	= drv->minor;//64
	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;//设置flag,
	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;
		struct tty_port *port = &state->port;

		tty_port_init(port);//初始化uart_state内部的tty_port,同时初始化tty_port内部的tty_bufhead
		port->ops = &uart_port_ops;//设置port的ops
		port->close_delay     = HZ / 2;	/* .5 seconds */
		port->closing_wait    = 30 * HZ;/* 30 seconds */
	}

	retval = tty_register_driver(normal);//注册tty驱动
	if (retval >= 0)
		return retval;

	for (i = 0; i < drv->nr; i++)
		tty_port_destroy(&drv->state[i].port);
	put_tty_driver(normal);
out_kfree:
	kfree(drv->state);
out:
	return -ENOMEM;
}
//分配tty_driver flags = 0, lines = 3
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
		unsigned long flags)
{
	struct tty_driver *driver;
	unsigned int cdevs = 1;
	int err;

	if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
		return ERR_PTR(-EINVAL);

	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
	if (!driver)
		return ERR_PTR(-ENOMEM);

	kref_init(&driver->kref);
	driver->magic = TTY_DRIVER_MAGIC;
	driver->num = lines;//设置tty_driver支持的设备数量
	driver->owner = owner;
	driver->flags = flags;

	if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
		driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
				GFP_KERNEL);//分配长度为3的struct tty_struct *的数组
		driver->termios = kcalloc(lines, sizeof(*driver->termios),
				GFP_KERNEL);//分配终端属性结构struct ktermios *数组
		if (!driver->ttys || !driver->termios) {
			err = -ENOMEM;
			goto err_free_all;
		}
	}

	if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
		driver->ports = kcalloc(lines, sizeof(*driver->ports),
				GFP_KERNEL);//分配长度为3的struct tty_port *数组
		if (!driver->ports) {
			err = -ENOMEM;
			goto err_free_all;
		}
		cdevs = lines;
	}

	driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);//分配长度为三的字符设备结构
	if (!driver->cdevs) {
		err = -ENOMEM;
		goto err_free_all;
	}

	return driver;
err_free_all:
	kfree(driver->ports);
	kfree(driver->ttys);
	kfree(driver->termios);
	kfree(driver);
	return ERR_PTR(err);
}
EXPORT_SYMBOL(__tty_alloc_driver);
//注册tty驱动函数
/*
 * Called by a tty driver to register itself.
 */
int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	struct device *d;

	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);
		}
	} else {
		dev = MKDEV(driver->major, driver->minor_start);//设备号4:64
		error = register_chrdev_region(dev, driver->num, driver->name);//注册字符设备驱动设备号空间
	}
	if (error < 0)
		goto err;

	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {//没有执行
		error = tty_cdev_add(driver, dev, 0, driver->num);
		if (error)
			goto err_unreg_char;
	}

	mutex_lock(&tty_mutex);
	list_add(&driver->tty_drivers, &tty_drivers);//将新的ttydriver加入到tty_drivers全局链表中,查找driver时会根据设备号遍历这个链表。
	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_unreg_devs;
			}
		}
	}
	proc_tty_register_driver(driver);//在/proc/tty/driver中创建proc entry
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

err_unreg_devs:
	for (i--; i >= 0; i--)
		tty_unregister_device(driver, i);

	mutex_lock(&tty_mutex);
	list_del(&driver->tty_drivers);
	mutex_unlock(&tty_mutex);

err_unreg_char:
	unregister_chrdev_region(dev, driver->num);
err:
	return error;
}
EXPORT_SYMBOL(tty_register_driver);
//注册uart_driver 完成

添加port部分:

static void __init
serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
	int i;

	for (i = 0; i < nr_uarts; i++) {
		struct uart_8250_port *up = &serial8250_ports[i];

		if (up->port.dev)
			continue;

		up->port.dev = dev;

		if (up->port.flags & UPF_FIXED_TYPE)
			serial8250_init_fixed_type_port(up, up->port.type);

		uart_add_one_port(drv, &up->port);
	}
}

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

	BUG_ON(in_interrupt());

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

	state = drv->state + uport->line;
	port = &state->port;//参考系统框图tty_port 和uart_port建立的直观联系

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

	state->uart_port = uport;
	state->pm_state = UART_PM_STATE_UNDEFINED;

	uport->cons = drv->cons;
	uport->state = state;

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

	uart_configure_port(drv, state, uport);

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

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

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

	return ret;
}

static void
uart_configure_port(struct uart_driver *drv, struct uart_state *state,
		    struct uart_port *port)
{
	unsigned int flags;

	/*
	 * If there isn't a port here, don't do anything further.
	 */
	if (!port->iobase && !port->mapbase && !port->membase)
		return;

	/*
	 * Now do the auto configuration stuff.  Note that config_port
	 * is expected to claim the resources and map the port for us.
	 */
	flags = 0;
	if (port->flags & UPF_AUTO_IRQ)
		flags |= UART_CONFIG_IRQ;
	if (port->flags & UPF_BOOT_AUTOCONF) {
		if (!(port->flags & UPF_FIXED_TYPE)) {
			port->type = PORT_UNKNOWN;
			flags |= UART_CONFIG_TYPE;
		}
		port->ops->config_port(port, flags);
	}

	if (port->type != PORT_UNKNOWN) {
		unsigned long flags;

		uart_report_port(drv, port); //输出注册port的打印信息

		/* Power up port for set_mctrl() */
		uart_change_pm(state, UART_PM_STATE_ON);

		/*
		 * Ensure that the modem control lines are de-activated.
		 * keep the DTR setting that is set in uart_set_options()
		 * We probably don't need a spinlock around this, but
		 */
		spin_lock_irqsave(&port->lock, flags);
		port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);
		spin_unlock_irqrestore(&port->lock, flags);

		/*
		 * If this driver supports console, and it hasn't been
		 * successfully registered yet, try to re-register it.
		 * It may be that the port was not available.
		 */
		if (port->cons && !(port->cons->flags & CON_ENABLED))
			register_console(port->cons);//注册console

		/*
		 * Power down all ports by default, except the
		 * console if we have one.
		 */
		if (!uart_console(port))
			uart_change_pm(state, UART_PM_STATE_OFF);
	}
}

console 相关:

struct console {
	char	name[16];
	void	(*write)(struct console *, const char *, unsigned);
	int	(*read)(struct console *, char *, unsigned);
	struct tty_driver *(*device)(struct console *, int *);
	void	(*unblank)(void);
	int	(*setup)(struct console *, char *);
	int	(*early_setup)(void);
	short	flags;
	short	index;
	int	cflag;
	void	*data;
	struct	 console *next;
};

static struct console serial8250_console = {
	.name		= "ttyS",
	.write		= serial8250_console_write,
	.device		= uart_console_device,
	.setup		= serial8250_console_setup,
	.early_setup	= serial8250_console_early_setup,
	.flags		= CON_PRINTBUFFER | CON_ANYTIME,
	.index		= -1,
	.data		= &serial8250_reg,
};
//注册console
void register_console(struct console *newcon)
{
	int i;
	unsigned long flags;
	struct console *bcon = NULL;

	/*
	 * before we register a new CON_BOOT console, make sure we don't
	 * already have a valid console
	 */
	if (console_drivers && newcon->flags & CON_BOOT) { //此时console_driver有值, 此时注册的console不是boot console, console_driver是指向console的指针,管理console链表
		/* find the last or real console */
		for_each_console(bcon) {
			if (!(bcon->flags & CON_BOOT)) {
				printk(KERN_INFO "Too late to register bootconsole %s%d\n",
					newcon->name, newcon->index);
				return;
			}
		}
	}

	if (console_drivers && console_drivers->flags & CON_BOOT)//此时console是boot console
		bcon = console_drivers; 

	if (preferred_console < 0 || bcon || !console_drivers)
		preferred_console = selected_console;

	if (newcon->early_setup)//无任何功能的函数
		newcon->early_setup();

	/*
	 *	See if we want to use this console driver. If we
	 *	didn't select a console we take the first one
	 *	that registers here.
	 */
	if (preferred_console < 0) { //会执行
		if (newcon->index < 0)
			newcon->index = 0;
		if (newcon->setup == NULL ||
		    newcon->setup(newcon, NULL) == 0) { 
			newcon->flags |= CON_ENABLED;//设置为使能
			if (newcon->device) {
				newcon->flags |= CON_CONSDEV; //此console确为console
				preferred_console = 0; //设置找到标志
			}
		}
	}

	/*
	 *	See if this console matches one we selected on
	 *	the command line.
	 */
	for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
			i++) { //console_cmdline 从传递给kernel的参数中获得信息
		if (strcmp(console_cmdline[i].name, newcon->name) != 0)
			continue;
		if (newcon->index >= 0 &&
		    newcon->index != console_cmdline[i].index) //参数为ttys1 此时newcon index =-1
			continue;
		if (newcon->index < 0)
			newcon->index = console_cmdline[i].index; //设置index = 1
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
		if (console_cmdline[i].brl_options) {
			newcon->flags |= CON_BRL;
			braille_register_console(newcon,
					console_cmdline[i].index,
					console_cmdline[i].options,
					console_cmdline[i].brl_options);
			return;
		}
#endif
		if (newcon->setup &&
		    newcon->setup(newcon, console_cmdline[i].options) != 0) //重新设置波特率等信息
			break;
		newcon->flags |= CON_ENABLED;
		newcon->index = console_cmdline[i].index;
		if (i == selected_console) {
			newcon->flags |= CON_CONSDEV;
			preferred_console = selected_console;
		}
		break;
	}

	if (!(newcon->flags & CON_ENABLED))
		return;

	/*
	 * If we have a bootconsole, and are switching to a real console,
	 * don't print everything out again, since when the boot console, and
	 * the real console are the same physical device, it's annoying to
	 * see the beginning boot messages twice
	 */
	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
		newcon->flags &= ~CON_PRINTBUFFER;

	/*
	 *	Put this console in the list - keep the
	 *	preferred driver at the head of the list.
	 */
	console_lock();
	if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
		newcon->next = console_drivers; //加入console_driver 链表
		console_drivers = newcon;
		if (newcon->next)
			newcon->next->flags &= ~CON_CONSDEV; //取消boot console的系统console标志
	} else {
		newcon->next = console_drivers->next;
		console_drivers->next = newcon;
	}
	if (newcon->flags & CON_PRINTBUFFER) {
		/*
		 * console_unlock(); will print out the buffered messages
		 * for us.
		 */
		raw_spin_lock_irqsave(&logbuf_lock, flags);
		console_seq = syslog_seq;
		console_idx = syslog_idx;
		console_prev = syslog_prev;
		raw_spin_unlock_irqrestore(&logbuf_lock, flags);
		/*
		 * We're about to replay the log buffer.  Only do this to the
		 * just-registered console to avoid excessive message spam to
		 * the already-registered consoles.
		 */
		exclusive_console = newcon;
	}
	console_unlock();
	console_sysfs_notify();

	/*
	 * By unregistering the bootconsoles after we enable the real console
	 * we get the "console xxx enabled" message on all the consoles -
	 * boot consoles, real consoles, etc - this is to ensure that end
	 * users know there might be something in the kernel's log buffer that
	 * went to the bootconsole (that they do not see on the real console)
	 */
	if (bcon &&
	    ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
	    !keep_bootcon) {
		/* we need to iterate through twice, to make sure we print
		 * everything out, before we unregister the console(s)
		 */
		printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",
			newcon->name, newcon->index); //打印注册新console成功
		for_each_console(bcon)
			if (bcon->flags & CON_BOOT)
				unregister_console(bcon);
	} else {
		printk(KERN_INFO "%sconsole [%s%d] enabled\n",
			(newcon->flags & CON_BOOT) ? "boot" : "" ,
			newcon->name, newcon->index);
	}
}
EXPORT_SYMBOL(register_console);

struct device *tty_port_register_device_attr(struct tty_port *port,
		struct tty_driver *driver, unsigned index,
		struct device *device, void *drvdata,
		const struct attribute_group **attr_grp)
{
	tty_port_link_device(port, driver, index);
	return tty_register_device_attr(driver, index, device, drvdata,
			attr_grp);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_attr);

void tty_port_link_device(struct tty_port *port,
		struct tty_driver *driver, unsigned index)
{
	if (WARN_ON(index >= driver->num))
		return;
	driver->ports[index] = port;//将tty_port保存到tty_driver的指针数组中
}

struct device *tty_register_device_attr(struct tty_driver *driver,
				   unsigned index, struct device *device,
				   void *drvdata,
				   const struct attribute_group **attr_grp)
{
	char name[64];
	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
	struct device *dev = NULL;
	int retval = -ENODEV;
	bool cdev = false;

	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);//按设备号,根据tty_driver名ttyS设置名称

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {//将之前创建的三个字符设备驱动加入到设备驱动框架中
		retval = tty_cdev_add(driver, devt, index, 1);
		if (retval)
			goto error;
		cdev = true;
	}

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	dev->devt = devt; //设置设备号
	dev->class = tty_class; //将会出现在tty class目录下
	dev->parent = device; //父设备是serial8250 platform device
	dev->release = tty_device_create_release;
	dev_set_name(dev, "%s", name); //设置设备名称
	dev->groups = attr_grp;// 属性文件组
	dev_set_drvdata(dev, drvdata);

	retval = device_register(dev); //添加到sys设备框架中, 之后通过mdev将会在/dev创建ttySn设备
	if (retval)
		goto error;

	return dev;

error:
	put_device(dev);
	if (cdev)
		cdev_del(&driver->cdevs[index]);
	return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(tty_register_device_attr);

到此为止,三个串口设备注册完成。系统启动后,在dev目录下会生成ttyS0, ttyS1, ttyS2,代表三个串口的三个设备节点。
总结下uart driver框架建立的全过程:
1.注册uart driver, 创建关联的tty driver和tty driver相关的字符设备驱动, 以及uart_state. uart_state 内部有tty_port.
2. 注册所有的uart port, 将uart port与tty port关联起来,同时注册系统参数指定的console, 最后将三个字符设备驱动和表示三个port的device注册到驱动模型中。
完成uart硬件设备注册和uart driver 框架建立后,就可以通过打开,读写ttySn节点来操作串口。(必要的serial_in, serial_out等硬件相关函数已经填好)。

ttySn设备的操作在下一节整理介绍。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
pdf,epub,mobi Three in one zip file. Jump right into the pro-level guts of the Raspberry Pi with complete schematics and detailed hardware explanations as your guide. You'll tinker with runlevels, reporting voltages and temperatures, and work on a variety of project examples that you can tune for your own project ideas.. This book is fully updated for the latest Pi boards with three chapters dedicated to GPIO to help you master key aspects of the Raspberry Pi. You'll work with Linux driver information and explore the different Raspberry Pi models, including the Pi Zero, Pi Zero W, Pi 2, Pi3 B and Pi3 B+. You'll also review a variety of project examples that you can tune for your own project ideas. Other topics covered include the 1-Wire driver interface, how to configure a serial Linux console, and cross-compile code, including the Linux kernel. You'll find yourself turning to Advanced Raspberry Pi over and over again for both inspiration and reference. Whether you're an electronics professional, an entrepreneurial maker, or just looking for more detailed information on the Raspberry Pi, this is exactly the book for you.What You'll LearnMaster I2C and SPI communications from Raspbian Linux in CProgram USB peripherals, such as a 5-inch LCD panel with touch control and the Pi cameraStudy GPIO hardware, the sysfs driver interface and direct access from C programsUse and program the UART serial device. Who This Book Is ForAdvanced Raspberry Pi users who have experience doing basic projects and want to take their projects further.
8.1 C++ Samples 的目录 2018\09\20 周四 10:11 . 2018\09\20 周四 10:11 .. 2018\09\20 周四 10:11 0 1.txt 2018\09\10 周一 17:27 AC97 Driver Sample 2018\09\10 周一 17:27 AddFilter Storage Filter Tool 2018\09\10 周一 17:27 AMCC5933 - PCI Device Driver Using WDF 2018\09\10 周一 17:27 Async Notification Sample 2018\09\10 周一 17:27 Audio Adapters Samples 2018\09\10 周一 17:27 AvScan File System Minifilter Driver 2018\09\10 周一 17:27 AVStream filter-centric simulated capture sample driver (Avssamp) 2018\09\10 周一 17:27 AVStream simulated hardware sample driver (Avshws) 2018\09\10 周一 17:27 Bindview Network Configuration Utility 2018\09\10 周一 17:27 Bluetooth Echo L2CAP Profile Driver 2018\09\10 周一 17:27 Bluetooth Serial HCI Bus Driver 2018\09\10 周一 17:27 Cancel-Safe IRP Queue Sample 2018\09\10 周一 17:27 CancelSafe File System Minifilter Driver 2018\09\10 周一 17:27 CDFS File System Driver 2018\09\10 周一 17:27 CDO File System Minifilter Driver 2018\09\10 周一 17:27 CDROM Storage Class Driver 2018\09\10 周一 17:27 Change File System Minifilter Driver 2018\09\10 周一 17:27 ClassPnP Storage Class Driver Library 2018\09\10 周一 17:27 Common Property Sheet UI Sample 2018\09\10 周一 17:27 Connection-less NDIS 6.0 Sample Protocol Driver 2018\09\10 周一 17:27 Ctx File System Minifilter Driver 2018\09\10 周一 17:27 Delete File System Minifilter Driver 2018\09\10 周一 17:27 Device Console (DevCon) Tool 2018\09\10 周一 17:27 DirectMusic Software Synthesizer Sample 2018\09\10 周一 17:27 DirectMusic UART Driver Sample 2018\09\10 周一 17:27 Disk Class Driver 2018\09\10 周一 17:27 Driver Install Frameworks API (DIFxAPI) Sample 2018\09\10 周一 17:27 Driver MFT Sample 2018\09\10 周一 17:27 Early Launch Anti-Malware Driver 2018\09\10 周一 17:27 Echo Sample (UMDF Version 1) 2018\09\10 周一 17:27 Echo Sample (UMDF Version 2) 2018\09\10 周一 17:27 Eventdrv 2018\09\10 周一 17:27 Fakemodem Driver 2018\09\10 周一 17:27 fastfat File System Driver 2018\09\10 周一 17:27 FileHistory Sample 2018\09\10 周一 17:27 FM Synthesizer Driver Sample 2018\09\10 周一 17:27 Generic Text-Only Driver 2018\09\10 周一 17:27 GenPrint Print Processor Sample 2018\09\10 周一 17:27 GPIO Sample Drivers 2018\09\10 周一 17:27 Hardware Event Sample 2018\09\10 周一 17:27 HBtnKey Input Driver 2018\09\10 周一 17:27 HClient sample application 2018\09\10 周一 17:27 HID Minidriver Sample (UMDF Version 1) 2018\09\10 周一 17:27 HIDUSBFX2 sample driver 2018\09\10 周一 17:27 Hyper-V Extensible Switch extension filter driver 2018\09\10 周一 17:27 IOCTL 2018\09\10 周一 17:27 iSCSI WMI Client 2018\09\10 周一 17:27 Kernel Counter Sample (Kcs) 2018\09\10 周一 17:27 Kernel mode display-only miniport driver (KMDOD) sample 2018\09\10 周一 17:27 Keyboard Input WDF Filter Driver (Kbfiltr) 2018\09\10 周一 17:27 Keyboard Layout Samples 2018\09\10 周一 17:27 KMDF Echo Sample 2018\09\10 周一 17:27 KMDF filter driver for a HID device 2018\09\10 周一 17:27 KMDF Power Framework (PoFx) Sample 2018\09\10 周一 17:27 LSI_U3 StorPort Miniport Driver 2018\09\10 周一 17:27 Metadata Manager File System Minifilter Driver 2018\09\10 周一 17:27 Microsoft slate system virtual audio device driver sample 2018\09\10 周一 17:27 Microsoft Virtual Audio Device Driver Sample 2018\09\10 周一 17:27 Minispy File System Minifilter Driver 2018\09\10 周一 17:27 Mouse Input WDF Filter Driver (Moufiltr) 2018\09\10 周一 17:27 Msfilter Sample Codec 2018\09\10 周一 17:27 Msgsm610 Sample Codec 2018\09\10 周一 17:27 MSPLOT Plotter Driver Sample 2018\09\10 周一 17:27 Multipath IO (MPIO) DSM Sample 2018\09\10 周一 17:27 NameChanger File System Minifilter Driver 2018\09\10 周一 17:27 Native Wi-Fi Miniport Sample Driver 2018\09\10 周一 17:27 Native Wifi IHV Service 2018\09\10 周一 17:27 NDIS 6.0 Filter Driver 2018\09\10 周一 17:27 NDIS Connection-less Protocol Driver Sample 2018\09\10 周一 17:27 NDIS MUX Intermediate Driverand Notify Object 2018\09\10 周一 17:27 NDIS Virtual Miniport Driver 2018\09\10 周一 17:27 Near-Field Proximity Sample Driver (UMDF Version 1) 2018\09\10 周一 17:27 NONPNP 2018\09\10 周一 17:27 NullFilter File System Minifilter Driver 2018\09\10 周一 17:27 ObCallback Callback Registration Driver 2018\09\10 周一 17:27 OEM Printer Customization Plug-in Samples 2018\09\10 周一 17:27 OpenXPS Documents Print Sample 2018\09\10 周一 17:27 PassThrough File System Minifilter Driver 2018\09\10 周一 17:27 PCIDRV - WDF Driver for PCI Device 2018\09\10 周一 17:27 PCMCIA Smart Card Driver 2018\09\10 周一 17:27 PixLib sample 2018\09\10 周一 17:27 PLX9x5x PCI Driver 2018\09\10 周一 17:27 PortIO Sample Driver 2018\09\10 周一 17:27 Power Framework (PoFx) Sample (UMDF Version 2) 2018\09\10 周一 17:27 Print Auto Configuration Sample 2018\09\10 周一 17:27 Print Driver Constraints Sample 2018\09\10 周一 17:27 Print Driver INF Sample 2018\09\10 周一 17:27 Print Driver USB Monitor and Bidi Sample 2018\09\10 周一 17:27 Print Monitors Samples 2018\09\10 周一 17:27 Print Pipeline Simple Filter 2018\09\10 周一 17:27 Print Provider Sample Template 2018\09\10 周一 17:27 Print Queue Active Server Page Sample 2018\09\10 周一 17:27 Radio Switch Test Driver for OSR USB-FX2 Development Board 2018\09\10 周一 17:27 RAMDisk Storage Driver Sample 2018\09\10 周一 17:27 RegFltr Sample Driver 2018\09\10 周一 17:27 Sample Function Driver for OSR USB-FX2 (UMDF Version 2) 2018\09\10 周一 17:27 Sample KMDF Bus Driver for OSR USB-FX2 2018\09\10 周一 17:27 Sample KMDF Driver Implementing a WMI Data Provider 2018\09\10 周一 17:27 Sample KMDF Function Driver for OSR USB-FX2 2018\09\10 周一 17:27 Sample UMDF Filter above KMDF Function Driver for OSR USB-FX2 (UMDF Version 1) 2018\09\10 周一 17:27 Sample UMDF Filter above UMDF Function Driver for OSR USB-FX2 (UMDF Version 1) 2018\09\10 周一 17:27 Sample UMDF Function Driver for OSR USB-FX2 (UMDF Version 1) 2018\09\10 周一 17:27 Scanner File System Minifilter Driver 2018\09\10 周一 17:27 SCSI Pass-Through Interface Tool 2018\09\10 周一 17:27 SDV-FailDriver-KMDF 2018\09\10 周一 17:27 SDV-FailDriver-NDIS 2018\09\10 周一 17:27 SDV-FailDriver-STORPORT 2018\09\10 周一 17:27 SDV-FailDriver-WDM 2018\09\10 周一 17:27 Sensors Geolocation Sample Driver (UMDF Version 1) 2018\09\10 周一 17:27 Serenum sample 2018\09\10 周一 17:27 Serial Port Driver 2018\09\10 周一 17:27 SimRep File System Minifilter Driver 2018\09\10 周一 17:27 SimSensor Simulated Temperature Sensor Sample Driver 2018\09\10 周一 17:27 SimThermalClient Simulated Thermal Client Sample Driver 2018\09\10 周一 17:27 SkeletonI2C Sample Driver 2018\09\10 周一 17:27 SpbAccelerometer Sample Driver (UMDF Version 1) 2018\09\10 周一 17:27 SpbTestTool 2018\09\10 周一 17:27 Storage SDIO Driver 2018\09\10 周一 17:27 StorAhci StorPort Miniport Driver 2018\09\10 周一 17:27 Super Floppy (sfloppy) Storage Class Driver 2018\09\10 周一 17:27 SwapBuffer File System Minifilter Driver 2018\09\10 周一 17:27 System DMA 2018\09\10 周一 17:27 SystemTraceProvider 2018\09\10 周一 17:27 Toaster Package Sample 2018\09\10 周一 17:27 Toaster Sample (UMDF Version 2) 2018\09\10 周一 17:27 Toaster Sample Driver 2018\09\10 周一 17:27 Tracedrv 2018\09\10 周一 17:27 UMDF Driver Skeleton Sample (UMDF Version 1) 2018\09\10 周一 17:27 UMDF SocketEcho Sample (UMDF Version 1) 2018\09\10 周一 17:27 USB Host-Based Print Driver Sample 2018\09\10 周一 17:27 Usbsamp Generic USB Driver 2018\09\10 周一 17:27 USBView sample application 2018\09\10 周一 17:27 Virtual serial driver sample 2018\09\10 周一 17:27 WDF Hybrid 1394 Virtual Device Sample Driver 2018\09\10 周一 17:27 WDF Installation Package 2018\09\10 周一 17:27 WDF Sample Driver Learning Lab for OSR USB-FX2 2018\09\10 周一 17:27 Windows Biometric Driver Samples (UMDF Version 1) 2018\09\10 周一 17:27 Windows Filtering Platform MSN Messenger Monitor Sample 2018\09\10 周一 17:27 Windows Filtering Platform Packet Modification Sample 2018\09\10 周一 17:27 Windows Filtering Platform Sample 2018\09\10 周一 17:27 Windows Filtering Platform Stream Edit Sample 2018\09\10 周一 17:27 Windows Filtering Platform Traffic Inspection Sample 2018\09\10 周一 17:27 Windows Image Acquisition (WIA) Driver Samples 2018\09\10 周一 17:27 Windows Radio Management Sample 2018\09\10 周一 17:27 WMI ACPI Sample 2018\09\10 周一 17:27 WPD Basic Hardware Sample Driver (UMDF Version 1) 2018\09\10 周一 17:27 WPD multi-transport sample driver 2018\09\10 周一 17:27 WPD service sample driver 2018\09\10 周一 17:27 WPD WUDF sample driver 2018\09\10 周一 17:27 WPDHelloWorld sample driver for portable devices 2018\09\10 周一 17:27 WSDMon Bidi Extension Sample 2018\09\10 周一 17:27 WSK TCP Echo Server 2018\09\10 周一 17:27 XPS Documents Print Sample 2018\09\10 周一 17:27 XPS Rasterization Filter Service Sample 2018\09\10 周一 17:27 XPSDrv Driver and Filter Sample 1 个文件 0 字节 157 个目录 43,620,806,656 可用字节
Linux内核UART设备驱动注册流程如下: 1. 分配tty_driver结构体 在驱动初始化时,首先需要分配一个tty_driver结构体,该结构体描述了tty设备的驱动属性信息,包括驱动名称、打开、关闭、读写等操作的回调函数指针等。 2. 注册tty_driver 调用tty_register_driver函数,将tty_driver结构体注册到内核中,该函数会将tty_driver结构体添加到tty_drivers链表中,同时会创建一个tty_class结构体和一个tty_class_dev结构体,并将其关联起来。 3. 创建tty设备节点 调用tty_register_device函数,该函数会根据tty_driver结构体中的信息创建tty设备节点,并将其添加到tty_drivers链表中。 4. 设置tty设备驱动回调函数 在tty_driver结构体中设置相应的驱动回调函数,例如open、close、read、write等操作的回调函数指针。 5. 注册tty设备驱动与硬件设备的关联 在驱动初始化时,需要将tty设备驱动与硬件设备进行关联,通常是通过platform_device_register函数将platform_device结构体注册到内核中,并调用platform_driver_register函数将platform_driver结构体注册到内核中。 6. 实现tty设备驱动回调函数 在驱动初始化时,需要实现相应的tty设备驱动回调函数,例如open、close、read、write等操作的回调函数。当用户调用相应的操作时,内核会自动调用相应的回调函数执行相应的操作。 7. 注销tty设备驱动 在驱动卸载时,需要调用tty_unregister_driver函数注销tty_driver结构体,并释放相关资源。同时也需要注销与硬件设备的关联关系。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值