在Linux中,UART串口驅動徹底遵循tty驅動的框架結構,可是進行了底層操做的再次封裝,因此先介紹tty終端設備驅動。前端
1、終端設備node
1.串行端口終端(/dev/ttySACn)ios
2.僞終端(/dev/pty/)session
3.控制檯終端(/dev/ttyn,/dev/console)數據結構
2、驅動結構框架
1.tty分層結構以下圖所示:async
包含tty核心、tty線路規程、tty驅動,其中tty規程的工做是以特殊的方式格式化從一個用戶或者硬件接收到的數據,常採用一個協議轉換的形式,如PPP、Bluetooth。ide
2.tty主要源文件關係及數據流向以下圖:函數
特定的tty設備驅動的主體工做是填充tty_driver結構體中的成員,實現tty_operations結構體中的一系列成員函數this
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 minor_num; /* number of *possible* devices */ 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; struct ktermios **termios_locked; void *driver_state; /* * Driver methods */ const struct tty_operations *ops; struct list_head tty_drivers; };
tty_operations中的成員函數需在特定設備tty驅動模塊初始化函數中被賦值;
tty_struct結構體被tty核心用來保存當前tty端口的狀態。
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, struct file * file, unsigned int cmd, unsigned long arg); long (*compat_ioctl)(struct tty_struct *tty, struct file * file, 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, struct file *file); int (*tiocmset)(struct tty_struct *tty, struct file *file, 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); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); void (*poll_put_char)(struct tty_driver *driver, int line, char ch); #endif const struct file_operations *proc_fops; };
struct tty_struct { int magic; struct kref kref; struct tty_driver *driver; const struct tty_operations *ops; int index; /* Protects ldisc changes: Lock tty not pty */ struct mutex ldisc_mutex; 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_io.c:
【1】定義了tty 設備通用的file_operations結構體:
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, };
【2】實現了接口函數alloc_tty_driver()用於分配tty驅動:
/*tty driver 的全部操做都包含在 tty_driver 中。內核即供了一個名叫 alloc_tty_driver() 來分配這個 tty_driver。固然咱們也能夠在本身的驅動中將它定義成一個靜態的結構。對 tty_driver 進行一些必要的初始化以後,調用 tty_register_driver()將其註冊. */ struct tty_driver *alloc_tty_driver(int lines) { 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; /* later we'll move allocation of tables here */ } return driver; }
【3】實現了接口函數tty_set_operations()用於設置tty驅動操做:
/*將tty_operations結構體中的函數指針拷貝給tty_driver對應的函數指針*/ void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { driver->ops = op; };
【4】實現了接口函數tty_register_driver()用於註冊tty設備:
/* * Called by a tty driver to register itself. * 註冊 tty 驅動成功時返回 0;參數爲由 alloc_tty_driver()分配的 tty_driver 結構體指針。 *這個函數操做比較簡單。就是爲 tty_driver 建立字符設備。而後將字符設備的操做集指定 *爲 tty_fops.而且將 tty_driver 掛載到 tty_drivers 鏈表中。以設備號爲關鍵字找到對應 *的 driver. */ int tty_register_driver(struct tty_driver *driver) { int error; int i; dev_t dev; void **p = NULL; /*TTY_DRIVER_DEVPTS_MEM:使用 devpts 進行動態內存映射*/ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); if (!p) return -ENOMEM; } if (!driver->major) {/*若是沒有指定 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); error = register_chrdev_region(dev, driver->num, driver->name); } if (error < 0) { kfree(p);/*若是失敗則釋放申請的內存空間*/ return error; } if (p) {/*設置tty數據結構指針*/ driver->ttys = (struct tty_struct **)p;/*tty_struct結構體被tty核心用來保存當前端口的狀態*/ driver->termios = (struct ktermios **)(p + driver->num);/*ktermios保存當前的線路設置*/ } else { driver->ttys = NULL; driver->termios = NULL; } //註冊字符設備 cdev_init(&driver->cdev, &tty_fops); driver->cdev.owner = driver->owner; error = cdev_add(&driver->cdev, dev, driver->num); if (error) { unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; kfree(p); return error; } mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers);/*將 tty_driver 掛載到 tty_drivers 鏈表中*/ mutex_unlock(&tty_mutex); /*若是沒有指定 TTY_DRIVER_DYNAMIC_DEV.即動態設備管理 */ if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { for (i = 0; i < driver->num; i++) tty_register_device(driver, i, NULL);/*註冊tty設備*/ } /* * This function is called by tty_register_driver() to handle * registering the driver's /proc handler into /proc/tty/driver/<foo> */ proc_tty_register_driver(driver); driver->flags |= TTY_DRIVER_INSTALLED; return 0; }