Linux串口大致过程
来源:互联网 发布:智睿高清网络电视下载 编辑:程序博客网 时间:2024/05/23 14:56
drivers\tty\serial\imx.c
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
/*
* This determines how often we check the modem status signals
* for any change. They generally aren't connected to an IRQ
* so we have to poll them. We also check immediately before
* filling the TX fifo incase CTS has been dropped.
*/
#define MCTRL_TIMEOUT (250*HZ/1000)
#define DRIVER_NAME "IMX-uart"
#define UART_NR 8
static struct imx_port *imx_ports[UART_NR];
static struct uart_driver imx_reg = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = DEV_NAME,
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_ports),
.cons = IMX_CONSOLE,
};
static struct platform_driver serial_imx_driver = {
.probe = serial_imx_probe,
.remove = serial_imx_remove,
.suspend = serial_imx_suspend,
.resume = serial_imx_resume,
.id_table = imx_uart_devtype,
.driver = {
.name = "imx-uart",
.of_match_table = imx_uart_dt_ids,
},
};
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,//设置串口modem控制模式
.get_mctrl = imx_get_mctrl,//获取串口modem控制模式
.stop_tx = imx_stop_tx,//停止发送
.start_tx = imx_start_tx,//开始发送
.stop_rx = imx_stop_rx,//停止接收
.enable_ms = imx_enable_ms,//使能modem状态信息
.break_ctl = imx_break_ctl,
.startup = imx_startup,//打开串口
.shutdown = imx_shutdown,//关闭串口
.flush_buffer = imx_flush_buffer,
.set_termios = imx_set_termios,//设置串口参数
.type = imx_type,//io访问类型
.config_port = imx_config_port,//配置端口
.verify_port = imx_verify_port,//校验端口
#if defined(CONFIG_CONSOLE_POLL)
.poll_init = imx_poll_init,
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
#endif
};
imx_serial_init
uart_register_driver(&imx_reg);-------------------------------> uart_register_driver
platform_driver_register(&serial_imx_driver);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct uart_state {
struct tty_port port;
enum uart_pm_state pm_state;
struct circ_buf xmit;
struct uart_port *uart_port;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ktermios tty_std_termios = { /* for the benefit of tty drivers */
.c_iflag = ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN,
.c_cc = INIT_C_CC,
.c_ispeed = 38400,
.c_ospeed = 38400
};
// drivers\tty\serial\serial_core.c
int uart_register_driver(struct uart_driver *drv)
struct tty_driver *normal;
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);// 分配8个struct uart_state
normal = alloc_tty_driver(drv->nr);// drv->nr = 8
struct tty_driver *ret = tty_alloc_driver(lines, 0);// struct tty_driver,分配lines = 8
__tty_alloc_driver(lines, THIS_MODULE, flags)// lines = 8 flags=0
struct tty_driver *driver;
driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
driver->ttys = kcalloc(lines, sizeof(*driver->ttys), GFP_KERNEL);// 分配struct tty_struct **ttys;
driver->termios = kcalloc(lines, sizeof(*driver->termios), GFP_KERNEL);// 分配struct ktermios **termios;
}
if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
driver->ports = kcalloc(lines, sizeof(*driver->ports), GFP_KERNEL);// 分配struct tty_port **ports;
cdevs = lines;
}
driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);// 分配一个cdev
drv->tty_driver = normal;// uart_driver的tty_driver指向分配的normal(tty_driver)
normal->driver_name = drv->driver_name;// 驱动名"IMX-uart"
normal->name = drv->dev_name;// 设备名"ttymxc"
normal->major = drv->major;// 主设备号207
normal->minor_start = drv->minor;// 次设备好开始值16
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_driver的driver_state回指uart_driver
tty_set_operations(normal, &uart_ops);
driver->ops = op;// normal(tty_driver)的ops指向op----------------------> 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);// drivers\tty\tty_port.c
tty_buffer_init(port);// drivers\tty\tty_buffer.c
INIT_WORK(&buf->work, flush_to_ldisc);// 初始化接收数据工作队列 ---------------------------->接收数据中断(imx_rxint)时会调度工作队列
port->ops = &uart_port_ops;
}
retval = tty_register_driver(normal);// drivers\tty\tty_io.c
retval = tty_register_driver(normal);
register_chrdev_region(dev, driver->num, driver->name);// 驱动名"IMX-uart"
tty_cdev_add(driver, dev, 0, driver->num);
cdev_init(&driver->cdevs[index], &tty_fops);
driver->cdevs[index].owner = driver->owner;
return cdev_add(&driver->cdevs[index], dev, count);
list_add(&driver->tty_drivers, &tty_drivers);// 把tty_drivers挂到tty驱动列表上
for (i = 0; i < driver->num; i++) {
d = tty_register_device(driver, i, NULL);
struct device *dev = NULL;
retval = tty_cdev_add(driver, devt, index, 1);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
dev->devt = devt;
dev->class = tty_class;
dev->parent = 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);// 注册tty设备
proc_tty_register_driver(driver);
proc_create_data(driver->driver_name, 0, proc_tty_driver, driver->ops->proc_fops, driver);// /proc/tty/driver/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const struct tty_operations uart_ops = {// drivers\tty\serial\serial_core.c
.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
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
serial_imx_probe(struct platform_device *pdev)
struct imx_port *sport;
sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
// 设置struct uart_port
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX,
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_pops;// uart_ops
sport->port.rs485_config = imx_rs485_config;
sport->port.rs485.flags = SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
sport->port.flags = UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
/*
* Allocate the IRQ(s) i.MX1 has three interrupts whereas later
* chips only have one interrupt.
*/
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, dev_name(&pdev->dev), sport);// 接收数据中断
if (ret)
return ret;
ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, dev_name(&pdev->dev), sport);
if (ret)
return ret;
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, dev_name(&pdev->dev), sport);
if (ret)
return ret;
}
imx_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
dev_set_drvdata(&pdev->dev, data);
dev->driver_data = data;
return uart_add_one_port(&imx_reg, &sport->port);--------------------------------------->uart_add_one_port
// --------------------------------------------------------------------------------------------------------------------------------
static const struct file_operations tty_fops = {// drivers\tty\tty_io.c
.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,
};
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)// drivers\tty\serial\serial_core.c
struct uart_state *state;
struct tty_port *port;
state = drv->state + uport->line;// 获取当前的uart_state
port = &state->port;// 获取当前的struct tty_port
state->uart_port = uport;// uport = sport->port (struct uart_port)
uport->state = state;// uart_port的state回指state
uart_configure_port(drv, state, uport);
port->ops->config_port(port, flags);// 调用uart_port的uart_ops的config_port方法,即调用uart_ops imx_pops的imx_config_port(配置端口)
port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);// 调用uart_port的uart_ops的set_mctrl方法,即调用uart_ops imx_pops的imx_set_mctrl(设置串口modem控制模式)
tty_dev = tty_port_register_device_attr(port, drv->tty_driver, uport->line, uport->dev, port, uport->tty_groups);
tty_port_link_device(port, driver, index);
driver->ports[index] = port;
return tty_register_device_attr(driver, index, device, drvdata, attr_grp);// drivers\tty\tty_port.c
tty_cdev_add(driver, devt, index, 1);// drivers\tty\tty_io.c
cdev_init(&driver->cdevs[index], &tty_fops);
driver->cdevs[index].owner = driver->owner;
return cdev_add(&driver->cdevs[index], dev, count);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct tty_ldisc_ops tty_ldisc_N_TTY = {// drivers\rty\N_tty.c
.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,
.fasync = n_tty_fasync,
.receive_buf2 = n_tty_receive_buf2,
};
void __init console_init(void)// drivers\tty\tty_io.c
tty_ldisc_begin();
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
tty_ldiscs[disc] = new_ldisc;
new_ldisc->num = disc;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static const struct file_operations tty_fops = {// drivers\tty\tty_io.c
.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,
};
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");
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app->open:
tty_open->uart_open->startup(驱动实现的startup)
// 应用程序open->tty_open
static int tty_open(struct inode *inode, struct file *filp)// drivers\tty\tty_io.c
if (tty->ops->open)
retval = tty->ops->open(tty, filp);// tty_operations -> open() --> tty_operations-> open(即uart_open,drivers\tty\serial\serial_core.c)
static int uart_open(struct tty_struct *tty, struct file *filp)
uart_startup(tty, state, 0);
uart_port_startup(tty, state, init_hw);
state->xmit.buf = (unsigned char *) page;
uport->ops->startup(uport);// 调用到i.mx.c里面的uart_ops imx_pops中的imx_startup
static int imx_startup(struct uart_port *port)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app->read:
接收数据中断->把数据放入到tty_buffer类型的buf->data中
tty_read->ldisc.read(n_tty_read)->从buf->data中取数据(过程复杂)->put_user(x, ptr);
static irqreturn_t imx_rxint(int irq, void *dev_id)// imx.c
tty_insert_flip_char(port, rx, flg);// include\linux\tty_flip.h
tty_insert_flip_string_flags(port, &ch, &flag, 1);
memcpy(char_buf_ptr(tb, tb->used), chars, space);
(char *)char_buf_ptr(b, ofs) + b->size;// include\linux\tty.h ------------------------------> 存放读取到的数据
return ((unsigned char *)b->data) + ofs;
tty_flip_buffer_push(port);// drivers\tty\tty_buffer.c
tty_schedule_flip(port);
schedule_work(&buf->work);// 调度工作队列 --------------------------------> flush_to_ldisc
static void flush_to_ldisc(struct work_struct *work)// drivers\tty\tty_buffer.c 工作队列执行函数
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
struct tty_struct *tty;
struct tty_ldisc *disc;
tty = port->itty;
disc = tty_ldisc_ref(tty);
while(1) {
receive_buf(tty, head, count);// drivers\tty\tty_buffer.c
struct tty_ldisc *disc = tty->ldisc;
unsigned char *p = char_buf_ptr(head, head->read);------------------------> 获取存放已读取到的数据
char *f = NULL;
if (~head->flags & TTYB_NORMAL)
f = flag_buf_ptr(head, head->read);
if (disc->ops->receive_buf2)// drivers\tty\n_tty.c
count = disc->ops->receive_buf2(tty, p, f, count);
return n_tty_receive_buf_common(tty, cp, fp, count, 1);
else {
count = min_t(int, count, tty->receive_room);
if (count)
disc->ops->receive_buf(tty, p, f, count);// drivers\tty\n_tty.c
}
head->read += count;
return count;
}
static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)// drivers\tty\tty_io.c
ld = tty_ldisc_ref_wait(tty);
ld->ops->read(tty, file, buf, count);--------------->ldisc->read
ld = tty_ldisc_ref_wait(tty);
if (ld->ops->read)
i = ld->ops->read(tty, file, buf, count);-------------> n_tty_read
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)// drivers\tty\N_tty.c
DEFINE_WAIT_FUNC(wait, woken_wake_function);
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&ldata->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&ldata->atomic_read_lock))
return -ERESTARTSYS;
}
add_wait_queue(&tty->read_wait, &wait);
tty_put_user(tty, cs, b++)// drivers\tty\N_tty.c
tty_audit_add_data(tty, &x, 1, ldata->icanon);// drivers\tty\tty_audit.c
do {
memcpy(buf->data + buf->valid, data, run);// 把读到的数据存入到了tty_buffer类型的buf->data中
} while (size != 0);
return put_user(x, ptr);// 返回数据给APP
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app写:tty_write->ldisc.write->uart_write->start_tx(驱动实现的start_tx)
static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
ld = tty_ldisc_ref_wait(tty);
do_tty_write(ld->ops->write, tty, file, buf, count);// ld->ops->write--------------->ldisc->write(n_tty_write)
ret = write(tty, file, tty->write_buf, size);// --------------->ldisc->write
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)// drivers\tty\N_tty.c
add_wait_queue(&tty->write_wait, &wait);
tty->ops->write(tty, b, nr);// tty_operations->write(即uart_write: drivers\tty\serial\serial_core.c) ------------------> uart_write
static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)// drivers\tty\serial\serial_core.c
port = state->uart_port;
circ = &state->xmit;
__uart_start(tty);
port->ops->start_tx(port);// 调用驱动imx.c里面的imx_start_tx -------------------------> imx_start_tx
static void imx_start_tx(struct uart_port *port)// drivers\tty\serial\imx.c
schedule_delayed_work(&sport->tsk_dma_tx, 0);
dma_tx_work(struct work_struct *w)
sport->tx_bytes = uart_circ_chars_pending(xmit);// 将desc提交到DMA驱动等待队列
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app异步操作:
static int tty_fasync(int fd, struct file *filp, int on)// drivers\tty\tty_io.c
__tty_fasync(fd, filp, on);
fasync_helper(fd, filp, on, &tty->fasync);
ldisc = tty_ldisc_ref(tty);
ldisc->ops->fasync(tty, on);----------------> ldisc的n_tty_fasync
static void n_tty_fasync(struct tty_struct *tty, int on)// drivers\tty\n_tty.c
struct n_tty_data *ldata = tty->disc_data;
if (!waitqueue_active(&tty->read_wait)) {
if (on)
ldata->minimum_to_wake = 1;
else if (!tty->fasync)
ldata->minimum_to_wake = N_TTY_BUF_SIZE;
}
- Linux串口大致过程
- Linux串口添加过程
- Linux下编译jrtplib和jthread 大致过程 再修改
- spring Ioc大致过程
- springMvc源码大致过程
- [Linux]: ssh大致介绍
- V8编译的大致过程
- Tribon建工程大致过程
- 通过串口连接linux配置过程
- 通过串口连接linux配置过程
- arm linux 启动大致顺序
- 浏览器访问一个页面的大致过程
- STM32F207(1) 大致启动过程---初学者必备
- android的adapter的大致使用过程
- 一次MySQL查询的大致过程
- 复印机的工作过程大致如下
- 用Construct2制作游戏大致过程
- 通讯端口协议大致分成三类:串口,现场总线,以太网
- Java集合---ArrayList的实现原理
- 时间工具类java
- \0
- 关于U盘还原安装Mac系统
- 基于python的gensim word2vec训练词向量
- Linux串口大致过程
- 数据结构之线性表
- UIDatePicker
- Spring Boot——开发新一代Spring Java应用
- Java集合---LinkedList源码解析
- js函数--闭包和this对象
- UFLDL_Tutorial 笔记(deep learning绝佳的入门资料 )
- Apache ActiveMQ
- java编程之方法模板模型