Linux driver serial port (UART)

<uart driver overview>
In the embedded Linux system, the serial port is regarded as a terminal device, and the driver of the terminal device (tty) is divided into three parts:
tty_core
tty_disicipline 
tty_driver
 
Includes 3 structures: uart_driver, uart_port, uart_ops(include/serial_core.h). Therefore, the uart driver that implements a platform can implement these three structures.
 
<Relationship between uart_driver and tty_driver>
a: uart_driver structure 
Uart_driver contains serial device name, serial port driver name, primary and secondary device number, serial console (optional) and other information.Also encapsulated tty_driver (the underlying serial port driver does not need to care about tty_driver)
struct uart_driver
 {

    struct module  *owner; //The module that owns the uart_driver, typically THIS_MODULE 
    constchar *driver_name; //  Serial port driver name, serial device file name is based on the driver name 
    constchar *dev_name; //  Serial device name 
    int major; //Main device number 
    int minor; //Secondary device number 
    int nr; //  The number of serial ports supported by the uart_driver (maximum) 
    struct console  *cons;//  Its corresponding console. If the uart_driver supports serial console, otherwise it is NULL 
    .............................
    struct uart_state  *state;
    struct tty_driver  *tty_driver;   //Uart_driver encapsulates tty_driver so that the underlying uart driver does not care about ttr_driver.

};

 

 
(1) A tty driver must register/register tty_driver.
(2) A uart driver becomes registered/unregistered uart_driver.
Use the following interface:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
int tty_register_driver(struct tty_driver *drv);
void tty_unregister_driver(struct tty_driver *drv);

 


 
Actually, The operations of tty_register_driver() and tty_unregister_driver() are included in uart_register_driver() and uart_unregister_driver() respectively. Details are as follows:
 
 
<uart_port>
a:uart_port is used forDescribe a UART port(directly corresponds to a serial port) I / O port or I / O memory address, FIFO size, port type and other information.
struct uart_port {
spinlock_t lock;/* Serial port lock */
unsignedint iobase;/*  IO port base address */
unsignedchar __iomem *membase;/*  IO memory base address, IO memory virtual base address after mapping (such as ioremap) */
unsignedint irq;/*  Interrupt number */
unsignedint uartclk;/*  Serial clock */
unsignedint fifosize;/*  Serial FIFO buffer size */
unsignedchar x_char;/*  Xon/xoff character */
unsignedchar regshift;/*  Register shift */
unsignedchar iotype;/*  IO access method */
unsignedchar unused1;

#define  UPIO_PORT (0)/* IO port */
#define UPIO_HUB6 (1)
#define  UPIO_MEM (2)/* IO memory */
#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 */

unsignedint read_status_mask;/*  Concerned about Rx error status */
unsignedint ignore_status_mask;/*  Ignored Rx error status */
struct uart_info *info;        //Important, see below
struct uart_icount  icount;   /*  The counter uart_icount is a serial information counter, which contains the transmitted character count, the received character count, and so on. In the serial port's transmit interrupt handler and receive interrupt handler, we need to manage these counts.*/ 

struct console *cons;/*  Console structure */
#ifdefCONFIG_SERIAL_CORE_CONSOLE
unsignedlong sysrq;/* sysrq timeout */
#endif

upf_t flags;

#define UPF_FOURPORT ((__forceupf_t)(1 << 1))
#define UPF_SAK ((__forceupf_t)(1 << 2))
#define UPF_SPD_MASK ((__forceupf_t)(0x1030))
#define UPF_SPD_HI ((__forceupf_t)(0x0010))
#define UPF_SPD_VHI ((__forceupf_t)(0x0020))
#define UPF_SPD_CUST ((__forceupf_t)(0x0030))
#define UPF_SPD_SHI ((__forceupf_t)(0x1000))
#define UPF_SPD_WARP ((__forceupf_t)(0x1010))
#define UPF_SKIP_TEST ((__forceupf_t)(1 << 6))
#define UPF_AUTO_IRQ ((__forceupf_t)(1 << 7))
#define UPF_HARDPPS_CD ((__forceupf_t)(1 << 11))
#define UPF_LOW_LATENCY ((__forceupf_t)(1 << 13))
#define UPF_BUGGY_UART ((__forceupf_t)(1 << 14))
#define UPF_MAGIC_MULTIPLIER((__force upf_t)(1 << 16))
#define UPF_CONS_FLOW ((__forceupf_t)(1 << 23))
#define UPF_SHARE_IRQ ((__forceupf_t)(1 << 24))
#define UPF_BOOT_AUTOCONF ((__forceupf_t)(1 << 28))
#define UPF_FIXED_PORT ((__forceupf_t)(1 << 29))
#define UPF_DEAD ((__forceupf_t)(1 << 30))
#define UPF_IOREMAP ((__forceupf_t)(1 << 31))

#define UPF_CHANGE_MASK ((__forceupf_t)(0x17fff))
#define UPF_USR_MASK ((__forceupf_t)(UPF_SPD_MASK|UPF_LOW_LATENCY))

unsigned int mctrl;/*  Current moden setting */
unsigned int timeout;/* character-based timeout */
unsigned int type;/*  Port type */
const struct uart_ops *ops;/*  Serial port operation function set */
unsigned int custom_divisor;
unsigned int  line;/*  Port index */
resource_size_t mapbase;/*  IO memory physical base address, available 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;/*  Port private data, generally platform data pointer */
};

 


 
b: The serial port core layer provides the following function to add 1 port:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
(1) The call to the above function should occur after uart_register_driver(). One of the most important functions of uart_add_one_port() is to encapsulate tty_register_device().
(2) The "inverse function" of uart_add_one_port() is uart_remove_one_port(), which calls tty_unregister_device(). The prototype is:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
 
c:struct uart_info{}
Uart_info has two members in the underlying serial port driver: xmit and tty. When the user space program sends data through the serial port, the upper layer driver saves the user data in xmit; and the serial port sends the interrupt processing function to obtain the user data through xmit and send them out. The serial port receiving interrupt processing function needs to pass the received data to the row rule layer through the tty.
struct uart_info {
struct tty_struct *tty;   //accept
struct circ_buf xmit;//Upper level needssend
uif_t flags;
/*
* Definitions for info->flags. These are _private_ to serial_core,and
* are specific to this structure. They may be queried by low leveldrivers.
*/
#define UIF_CHECK_CD ((__force uif_t)(<< 25))
#define UIF_CTS_FLOW ((__force uif_t)(<< 26))
#define UIF_NORMAL_ACTIVE ((__force uif_t)(<< 29))
#define UIF_INITIALIZED ((__force uif_t)(<< 31))
#define UIF_SUSPENDED ((__force uif_t)(<< 30))
int blocked_open;
struct tasklet_struct tlet;    / / The upper driver task waiting queue
wait_queue_head_t open_wait;
wait_queue_head_t delta_msr_wait;
};
<uart_port >

There is an important uart_ops in Uart_port, the underlying hardware needs to be implemented:

struct uart_ops {
unsignedint(*tx_empty)(struct uart_port *); /* Serial portTx FIFOWhether the cache is empty */
void(*set_mctrl)(struct uart_port *,unsignedint mctrl);/* Set the serial portmodemcontrol */
unsignedint(*get_mctrl)(struct uart_port *);/* Get the serial port modemControl */
void(*stop_tx)(struct uart_port *);/* Prevent serial port from sending data */
void(*start_tx)(struct uart_port *);/* Enable serial port to send data */
void(*send_xchar)(struct uart_port *,char ch);/* Send xChar */
void(*stop_rx)(struct uart_port *);/* Do not allow serial port to receive data */
void(*enable_ms)(struct uart_port *);/* Enable modemStatus signal */
void(*break_ctl)(struct uart_port *,int ctl);/* Set breakSignal */
int(*startup)(struct uart_port *);/* Start the serial port,This function will be called when the application opens the serial device file */
void(*shutdown)(struct uart_port *);/* Close the serial port,This function will be called when the application closes the serial device file */
void(*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old);/* Set the serial port parameters */
void(*pm)(struct uart_port *,unsignedint state,
unsignedint oldstate);/* Serial power management */
int(*set_wake)(struct uart_port *,unsignedint state);/* */
constchar*(*type)(struct uart_port *);/* Returns a string describing the serial port type */
void(*release_port)(struct uart_port *);/* Release the IO that the serial port has applied for.Port/IO memory resources, if necessaryiounmap */
int(*request_port)(struct uart_port *);/* Apply for the necessary IOPort/IO memory resources, if necessary, can remap the serial port */
void(*config_port)(struct uart_port *,int);/* Perform the automatic configuration required for the serial port */
int(*verify_port)(struct uart_port *,struct serial_struct *);/* Verify the new serial port information */
int(*ioctl)(struct uart_port *,unsignedint,unsignedlong);/* IOControl */
};

 

 

 

 

 

 

 

Uart_driver passedSerial Core layerof

int uart_register_driver(struct uart_driver *drv)

Register with Core, passint uart_add_one_port(struct uart_driver *drv,struct uart_port *port)Add uart_port to the driver.

 

 

<The main work of the serial port driver >

a: Analysis
(1) After using the serial port core layer, the interface of the universal serial port tty driver layer, the main work to be completed by a serial port driver will include:
Define instances of structures such as uart_driver, uart_ops, uart_port, and initialize them where appropriate based on the specific hardware and driver.
(Of course, the driver of the specific device xxx can put these structures in the newly defined xxx_uart_driver, xxx_uart_ops, xxx_uart_port)
 
(2) Uart_register_driver() and uart_add_one_port() are called during module initialization to register the UART driver and add ports. When the module is unloaded, uart_unregister_driver() and uart_remove_one_port() are called to unregister the UART driver and remove the port.
 
(3) The member functions in uart_ops are implemented according to the datasheet of the specific hardware. The realization of these functions becomes the main work of the UART driver.
Serial port driver initialization process. In the module load function of the S3C2410 serial port driver, uart_register_driver() is called to register the s3c24xx_uart_drv uart_driver.
The initialization process is as follows:
s3c2410_serial_init()
→platform_driver_register()
S3c24xx_serial_probe() is executed and called in the s3c24xx_serial_probe() function
S3c24xx_serial_init_port() initializes the UART port and calls
Uart_add_one_port() add port
 
(4) serial port operation function, uart_ops interface function
The S3C2410 serial port driver uart_ops structure startup () member function s3c24xx_serial_startup () is used to start the port, apply for port transmission and reception interrupts, enable port transmission and reception.
 
 
b: code example
(1) Platform resources

static struct resource s3c2410_uart0_resource[] = {

   ………………………………

};

static struct resource s3c2410_uart1_resource[] = {

   ………………………………

};

static struct resource s3c2410_uart2_resource[] = {

   ………………………………

};

The platform device corresponding to each serial port is defined in the file linux/arch/arm/plat-samsung/dev-uart.c.

static struct platform_device s3c24xx_uart_device0 = {

.id = 0,

};

static struct platform_device s3c24xx_uart_device1 = {

.id = 1,

};

static struct platform_device s3c24xx_uart_device2 = {

.id = 2,

};

In the file linux/arch/arm/mach-s3c2440/mach-smdk2440.c there are initialization settings for some registers on the serial port.

static struct s3c2410_uartcfg smdk2440_uartcfgs[] __initdata = {

[0] = {

…………………………

},

[1] = {

…………………………

},

/* IR port */

[2] = {

…………………………

}

};

The function will be called in the file linux/arch/arm/mach-s3c2440/mach-smdk2440.c

S3c24xx_init_uarts() finally integrates the above hardware resources, initial configuration, and platform devices.

In the file linux/arch/arm/plat-s3c/init.c

static int __init s3c_arch_init(void)

{

………………………………

ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);

return ret;

}

This function adds the platform device corresponding to the serial port to the kernel.

(2) Analysis of the drive principle of serial devices

I think that the implementation of any device in Linux is "two lines." One is the establishment of the device model, and the other is the reading and writing of data streams. The same is true for the serial port driver.

 

Serial device model establishment:

The core structure of the serial device driver is in the file linux/drivers/serial/samsuing.c as follows

static struct uart_driver s3c24xx_uart_drv = {

.owner = THIS_MODULE,

.dev_name = "s3c2410_serial",  

.nr = CONFIG_SERIAL_SAMSUNG_UARTS,

.cons = S3C24XX_SERIAL_CONSOLE,

.driver_name = S3C24XX_SERIAL_NAME,

.major = S3C24XX_SERIAL_MAJOR,

.minor = S3C24XX_SERIAL_MINOR,

};

Serial port driver registration

static int __init s3c24xx_serial_modinit(void)

{

………………………………

ret = uart_register_driver(&s3c24xx_uart_drv);

………………………………

}

int uart_register_driver(struct uart_driver *drv)

{

………………………………

/ / Each port corresponds to a state

drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);

………………………………

Normal = alloc_tty_driver(drv->nr); //Assign the tty_driver corresponding to the serial port driver

………………………………

Drv->tty_driver = normal; //Let the drv->tty_driver field point to this tty_driver

………………………………

normal->driver_name = drv->driver_name;

normal->name = drv->dev_name;

normal->major = drv->major;

normal->minor_start = drv->minor;

………………………………

/ / Set the tty driver corresponding operation function set tty_operations (linux / drivers / char / core.c)

tty_set_operations(normal, &uart_ops); 

………………………………

Retval = tty_register_driver(normal); //Register the tty driver to the kernel

………………………………

}

(3) The serial port itself is a character device

In fact, the essence of the tty driver is a character device, in the file linux/drivers/char/tty_io.c, in order to operate the hardware by operating the device file.

 

int tty_register_driver(struct tty_driver *driver)

{

………………………………

cdev_init(&driver->cdev, &tty_fops);

driver->cdev.owner = driver->owner;

error = cdev_add(&driver->cdev, dev, driver->num);

………………………………

}

Its associated set of operation functions tty_fops is implemented in the file linux/drivers/char/tty_io.c

static const struct file_operations tty_fops = {

.llseek = no_llseek,

.read = tty_read,

.write = tty_write,

………………………………

.open = tty_open,

………………………………

};

The driver to this serial port is registered to the kernel as tty_driver. As mentioned above, each port of the serial port is added to the kernel as a platform device. Then these platform devices correspond to the platform device drivers that have them. In the file linux/drivers/serial/s3c2440.c there are:

 

static struct platform_driver s3c2440_serial_driver = {

.probe = s3c2440_serial_probe,

.remove = __devexit_p(s3c24xx_serial_remove),

.driver = {

.name = "s3c2440-uart",

.owner = THIS_MODULE,

},

};

 

His probe function is called when its driver matches the device.

static int s3c2440_serial_probe(struct platform_device *dev)

{

return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);

}

Each port has a structure describing it s3c24xx_uart_port in the file linux/drivers/serial/samsuing.c

static 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, // interrupt number for this port

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops, //The set of operation functions for this port

.flags = UPF_BOOT_AUTOCONF,

.line = 0, //port number

}

},

………………………………

The specific work of the above probe function is done by the function s3c24xx_serial_probe().

int s3c24xx_serial_probe(struct platform_device *dev,struct s3c24xx_uart_info *info)

 

{

………………………………

/ / Based on the hardware resources provided by the platform device and other information to initialize some fields in the port description structure

ret = s3c24xx_serial_init_port(ourport, info, dev);

/ / Registered serial port driver in front, here to register serial device

uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);

………………………………

}

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

{

 ………………………………

/ / Said that the serial port driver is tty_driver, here you can see that the serial device is actually tty_dev

tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);

………………………………

}

Serial data stream analysis:

In the establishment of the serial device model, three sets of operation functions are mentioned. The flow of data of uart_ops, tty_fops, s3c24xx_serial_ops is the call between these operation functions. The relationship of these calls is as follows:

 

You must open it before doing anything else with a device, linux/drivers/char/tty_io.c

static const struct file_operations tty_fops = {

………………………………

.open = tty_open,

………………………………

};

static int tty_open(struct inode *inode, struct file *filp)

{

………………………………

dev_t device = inode->i_rdev;

………………………………

Driver = get_tty_driver(device, &index); //Get its index number based on the port device number

………………………………

if (tty) {

………………………………

} else

Tty = tty_init_dev(driver, index, 0); //Create a tty_struct and initialize it

………………………………

}

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,int first_ok)

{

………………………………

Tty = alloc_tty_struct(); //Assign a tty_struct structure

/ / Initialization of some fields,

initialize_tty_struct(tty, driver, idx);

//The main job done is driver->ttys[idx] = tty;

retval = tty_driver_install_tty(driver, tty);

………………………………

/*

The main function of the following function is to call the line program open function ld->ops->open(tty).

An important data cache is allocated in this open function.

tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);

*/

retval = tty_ldisc_setup(tty, tty->link);

}

void initialize_tty_struct(struct tty_struct *tty,struct tty_driver *driver, int idx)

{

………………………………

/ / Get the line procedure operation function set tty_ldisc_N_TTY, and do the work tty->ldisc = ld;

tty_ldisc_init(tty);

………………………………

/*

The main function of the following function is INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);

Initialize a delay tty->buf.work and associate a handler flush_to_ldisc(), this function will

Used when reading data.

*/

tty_buffer_init(tty);

………………………………

tty->driver = driver; 

Tty->ops = driver->ops; //The ops here is struct tty_operations uart_ops

Tty->index = idx; //idx is the index number of the corresponding port of the tty_struct

tty_line_name(driver, idx, tty->name);

}

After the port device is opened, it can be read and written. Only the data is read here. In the file linux/drivers/char/tty_io.c,

static const struct file_operations tty_fops = {

………………………………

.read = tty_read,

………………………………

};

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,

loff_t *ppos)

{

………………………………

Ld = tty_ldisc_ref_wait(tty); //Get the line specification structure

If (ld->ops->read) //call the n_tty_read() function in the line procedure operation function set

i = (ld->ops->read)(tty, file, buf, count);

else

………………………………

}

In linux/drivers/char/N_tty.c:

struct tty_ldisc_ops tty_ldisc_N_TTY = {

………………………………

.open            = n_tty_open,

………………………………

.read            = n_tty_read,

………………………………

};

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,

 unsigned char __user *buf, size_t nr)

{

………………………………

while (nr) {

………………………………

if (tty->icanon && !L_EXTPROC(tty)) {

//If tty->icanon is set, it will be read one by one from the cache tty->read_buf[], and the correctness of each number read/data or other data types will be judged.

eol = test_and_clear_bit(tty->read_tail,tty->read_flags);

c = tty->read_buf[tty->read_tail];

………………………………

} else {

………………………………

/ / If you do not set tty-> icanon to read data from the cache tty->read_buf[], the reason is to read twice

/ / Because it is because the cache tty->read_buf[] is a ring cache

uncopied = copy_from_read_buf(tty, &b, &nr);

uncopied += copy_from_read_buf(tty, &b, &nr);

………………………………

}

}

………………………………

}

The user space is read from the cache tty->read_buf[], so is the data in the cache tty->read_buf[] coming from there? analyse as below:

Back to the file linux/drivers/serial/samsuing.c, the serial port data receiving interrupt processing function is implemented as follows:

This is where the most primitive data in the serial port flows in.

static irqreturn_t  s3c24xx_serial_rx_chars(int irq, void *dev_id)

{

………………………………

while (max_count-- > 0) {

………………………………

Ch = rd_regb(port, S3C2410_URXH); //Read one data from the data receive buffer

………………………………

Flag = TTY_NORMAL; / / ordinary data, it may be other data types are not discussed here

………………………………

/*

The main work done by the following function is like this

struct tty_buffer *tb = tty->buf.tail;

tb->flag_buf_ptr[tb->used] = flag;

tb->char_buf_ptr[tb->used++] = ch;

Insert the read data and the corresponding flag of the data into tty->buf.

*/

uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

}

Tty_flip_buffer_push(tty); // Pass the read max_count data to the upper layer.

 out:

return IRQ_HANDLED;

}

void tty_flip_buffer_push(struct tty_struct *tty)

{

………………………………

if (tty->low_latency)

flush_to_ldisc(&tty->buf.work.work);

else

schedule_delayed_work(&tty->buf.work, 1); 

/ / Here the delay work mentioned in the above serial device open, the work handler is also flush_to_ldisc.

}

static void flush_to_ldisc(struct work_struct *work)

{

………………………………

while ((head = tty->buf.head) != NULL) {

………………………………

char_buf = head->char_buf_ptr + head->read;

flag_buf = head->flag_buf_ptr + head->read;

………………………………

/ / In the serial port receiving interrupt processing function, save the received data and data flags to tty->buf, now

//These data and flags are passed up further with char_buf and flag_buf.

disc->ops->receive_buf(tty, char_buf,flag_buf, count);

spin_lock_irqsave(&tty->buf.lock, flags);

}

}

The function called disc->ops->receive_buf above is implemented in the file linux/drivers/char/N_tty.c

struct tty_ldisc_ops tty_ldisc_N_TTY = {

………………………………

.receive_buf     = n_tty_receive_buf,

………………………………

};

static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)

{

………………………………

// Now you can see the origin of the data in the buffer tty->read_buf.

if (tty->real_raw) {

/ / If you set tty-> real_raw to copy the above mentioned incoming data to tty->read_head.

//The data copy of the ring buffer needs to be done twice. The first copy is taken from the current position to the end of the cache. If there is still data that has not been tested and there is still space left in the buffer area, it will not be The completed data is taken to the beginning of the remaining space

//In the middle.

spin_lock_irqsave(&tty->read_lock, cpuflags);

i = min(N_TTY_BUF_SIZE - tty->read_cnt,N_TTY_BUF_SIZE - tty->read_head);

i = min(count, i);

memcpy(tty->read_buf + tty->read_head, cp, i);

tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);

tty->read_cnt += i;

cp += i;

count -= i;

i = min(N_TTY_BUF_SIZE - tty->read_cnt,

N_TTY_BUF_SIZE - tty->read_head);

i = min(count, i);

memcpy(tty->read_buf + tty->read_head, cp, i);

tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);

tty->read_cnt += i;

spin_unlock_irqrestore(&tty->read_lock, cpuflags);

} else {

for (i = count, p = cp, f = fp; i; i--, p++) {

// If tty->real_raw is not set, the data is obtained according to the incoming data flag classification.

………………………………

}

………………………………

}

………………………………

}

At this point, the entire process of data reading is over. It can be seen that the data reading can be divided into two phases. One phase is that the upper function reads data from the ring buffer tty->read_buf, and the second phase is that the underlying function takes the received data into the ring buffer tty->read_buf. .

 

 

Serial port driver

 

Serial port drivertty

 

Concept analysis:

inLinuxMedium, the terminal is a type of character device, which includes multiple types commonly usedttyReferred to as various interrupt devices

Serial terminal/dev/ttyS*):

The serial terminal is a terminal device connected by a serial port.LinuxEach serial device is regarded as a character device, and the device name corresponding to these serial ports is/dev/ttySAC0 with/dev/ttySAC1

 

Console terminal/dev/console):

inLinuxin,The output device device in the calculation is usually called the console terminal.(console).Specific hereprintk()The information output is related to the pen. note:/dev/console Is a virtual device, he needs to map to the realttyon. Such as through the kernel boot parametersconsole = ttySAC0"That isconsole Map to serial port0, often used by the kernel.

Note: The terminal here is a virtual device, virtual The device must be associated with the actual deviceconsole = ttySAC0 It is related when the system starts up.

 

Virtual terminal(/dev/tty*)

When the user logs in, the virtual terminal is used, and the shortcut key combination is used:ctcl+alt+[F1-F6]The key combination can be switched totty1,tty2,tty3Wait for the above.tty1-tty6Etc. called a virtual terminal, andtty0 Is an alias for the terminal currently in use. Mainly provided for use by the application.

 

 

ttyArchitecture

 

 

ttycore:

ttyThe core is the wholettyDevice abstraction and provide a single interface

ttyRoute planning:

ttyThe line procedure is the formatting of the transmission of data. For example, if a certain protocol needs to be implemented, the implementation code of the protocol needs to be placed at the location.
ttydrive:

Is orientedttyDevice hardware driver

 

 

note:LinuxGet backtracking information using functionsdump_stack()Used to display call information for various functions.

 

 

Serial port driver structure

Analysis: The serial port driver needs to provide the user with the function of reading data, writing data, opening the serial port and turning off the serial port. Before you open it, you need to initialize the serial port.

Important data structure:

UART Driver structure:struct uart_driver//One serial port corresponds to a serial port driver for describing the serial port structure

UART Port structure:struct uart_port//There are several serial ports that correspond to severalport

UART Related operation function structurestruct uart_ops//Corresponding to the operation function set supported by the relevant serial port

UART State structure:struct uart_state

UART Information structure:struct uart_info

 

Serial port initialization:

(1)Define and describe the serial port

struct uart_driver

(2)Register the serial port driver

uart_register_driver

1)Take out the corresponding serial port

2)Initialize the removed serial port

 

 

Serial drive driver:

System call process:

User useopen()Function to open device file

{note:

(1)Open the device file and there must be a corresponding device driver file open function:file_operations.

(2)currently usinguart_register_driver ()When registering a serial port driver, the function will call the function.tty_register_driver(), the function will callcdev_init()Function andcdev_add()

(3)Can be seen from herettyDevice is a character device

}

 

——>Kernel callcdevstructuremiddlefile-operationsPointer pointed to the structuretty-open()function-->tty-open()The function is called immediatelytty_struct structuremiddletty_operationsIn the structure pointed to by the pointeruart_open() function-->The function is then calleduart_satarup()function-->This function will be calleduart_port structuremiddleops The pointer points to the file manipulation function in the set of operation functions.

 

Open the steps:

Enable serial port receiving function——>Register an interrupt handler for the receipt of data ->Enable sending function ->Register interrupt handler for sending data

 

 

 

Serial port driver send data:

User through usewrite()Function implementation to send data

write()Function callcdevIn the structurefile_operations In the structure pointed to by the pointertty_write()function-->This function will be calledtty_ldisc_ops In the structurewriteThe function pointed to by the pointern_tty_write()function-->This function will be calleduart_tty Pointer in the structurewritePointed functionuart_write()function-->This function will call the functionuart_startup()function-->This function will be calleduart_portIn the structureopsThe file send function in the set of operation functions pointed to by the pointer

 

 

Note: When the interrupt is enabled, the driver findsFIFO If the amount of data is less than a certain value, an interrupt will be triggered, and the corresponding data transmission will be implemented in the interrupt handler.

 

Important concept- circular buffer

Using functionwrite()When, it will passttyThe processing of the core, in the process of processing, will put the data to be found in a place, this place is called circular buffer

 

note:FIFO The data in is first in, first out. For data in the circular buffer, when there is no data,tail withhead Is in the same location where the data is buffered, when loading data into it,head Change with the change of the write data, but the starting position of the transmitted data istail  

 

Data transmission process:

(1)Determine if there is a need to sendx_charIf used, passUTXHRegister transmission

note:x_charUsed to indicate the status of the receiving data end, whether it meets the conditions for receiving data

(2)If you want to judge whether the circular buffer or serial port status is not allowed to send data, you need to stop sending data.

(3)usewhile()Loop to send data, send rules<1>Cyclic buffering has data<2>The amount of data sent is less than256

note:<1>When sendingFIFOStop sending when the data is full

      <2>From the tail of the loop buffer (tailTake the data and send the data toUTXHDeposited

      <3>Use self-addition to adjust the position of the data in the loop buffer

(4)If the amount of data in the loop buffer is lower than256 Then wake up the process that is blocked at the time of sending (in the previous process, the process needs to send data in the king's circular buffer, but finds that there is not enough space in the circular buffer to store the data to be sent, the process goes to sleep)

(5)If the data in the circular buffer is empty, you need to turn off the interrupt, otherwise it will continue to generate interrupts affecting the stable operation of the system.

 

Serial driver data reception

The application must receive data to receiveread() function

Process:

User program call functionread()——>read() Function will call the structurefile_operationsinreadPointer pointed totty_read()function-->This function will call the structuretty_ldisc_ttyPointer in the structurereadPointedn_tty_read()function-->

 

 

n_tty_read()Function analysis:

(1)Set the application execution process state to (TASK_INTERRUPTIBLE

Note: set to this state but the program does not directly enter the blocking state, you need to enter the corresponding state in the process scheduling

(2)If no data is readable, this passes The scheduler implements the process to block the attitude

(3)in casereadbufData is read from it

 

Driver processing flow:

(1)ReadUFCONregister

(2)ReadUFSTATregister

(3)UFSTATmiddlefifocnt The size of the zero exit processing

(4)UFSTAT middleUERSATJudge error type

(5)FromURXHExtract the received data

(6)Flow control processing

(7)according toUERSTATValue records specific error types

(8)If you receive it issysrqCharacter, call special handler-uart_handle_sysrq_chgr()

(9)Send characters to the serial port driverbuffer,call functionuart_insert_char

(10)Serial port driverbufferData in the line protocolread_buf ,call functiontty_flip_buffer_push

 

Important concept--Flow Control:

When sending data to the receiver, if there is no free space in the receiver's data buffer, you need to inform the sender to stop sending data, otherwise the sender's data cannot be received.

Flow control method:

Software flow control: the receiving end sends data to the sending segment through the serial line.x_off

 

Hardware flow control: Connected by using hardware connections, when you can't receive data, it will make  The corresponding connection generates a high level, and the transmitter detects the pin every time it is sent.

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

 
 
 
 

Reprinted at: https://www.cnblogs.com/big-devil/p/8590050.html

Intelligent Recommendation

Linux drive - kernel UART serial driver analysis

Foreword The purpose of writing articles is to review your own learning process so that you can review and refer to it later. First, introduction The serial port is a very common peripheral, which is ...

Linux serial port UART programming-C language

Serial communication is the most commonly used communication method. This article will learn and record serial port programming for future reference. Common interface types 1.open serial port 2. Set u...

[Linux application] serial port UART programming

1 Introduction Universal Asynchronous Receiver / Transmitter, is commonly referred to as a UART) is a serial asynchronous transmission and reception protocol, which is very widespread. The UART workin...

More Recommendation

SAL32 HAL library serial port (USART/UART) driver BUG and solution

problem When using the serial port part of the HAL library, I found the following 6 serious bugs, which seriously affect the normal use! Here is a note. If a serial port error occurs during DMA transm...

HiSilicon HI35XX series newly added UART serial port driver

Hisilicon HI3519V101 adds serial device uart1 uart4 One, modify the device tree 1. Find the following three files, hisi-hi3519v101.dtsi, hisi-hi3519v101-demb.dts, hisi-hi3519v101-hmp-demb.dts 2. Modif...

linux serial port driver analysis

Click on the link to open https://www.cnblogs.com/chd-zhangbo/p/5410336.html A data structure, the serial port driver • UART driver structure:struct uart_driver drive • UART port structure: ...

linux serial port driver(1)

Our hardware schematic diagram is as follows No need for J19 If we use J18 J20 J22, we must convert the TTL level to RS232 level That big piece of U6 is the level conversion chip Our COM1 is level-shi...

Linux serial port driver problem

CH340 chip driver Question 1: Ubuntu shows that the serial port /dev/ttyUSB0 is normal, and the program is open failed Solution: Increase permissions sudo ./executable The fundamental method: Add a 20...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top