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. };
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);
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 */ };
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)(1 << 25))
#define UIF_CTS_FLOW ((__force uif_t)(1 << 26))
#define UIF_NORMAL_ACTIVE ((__force uif_t)(1 << 29))
#define UIF_INITIALIZED ((__force uif_t)(1 << 31))
#define UIF_SUSPENDED ((__force uif_t)(1 << 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;
};
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 >
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 parameters“console = 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;">