Serial Port Driver Program Design---Serial port open, send, receive (next)

Source: Internet
Author: User
Tags goto mutex

On a blog analysis of the serial drive initialization section, the following step-by-step analysis of serial port drive in the open serial, data transmission and reception.

Initialize main workflow:


First to analyze the serial port to open the operation process, or first figure:



Here, the analysis is inseparable from the last blog in two important pictures:



The important data structure of serial operation:


From the previous serial port driver analysis that in the SAMSUNG.C module initialization has a job is to register a serial driver,


Jump into this function uart_register_driver can see a function:

retval = Tty_register_driver (normal);

Jump to this function, put the source code here:

int tty_register_driver (struct tty_driver *driver) {int error;
	int i;
	dev_t Dev;
	void **p = NULL;

	struct device *d; 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) {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) {driver->ttys = (struct tty_struct * *) p;
	Driver->termios = (struct Ktermios * *) (p + driver->num);
		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);

	Mutex_unlock (&tty_mutex); if (!) ( Driver->flags & Tty_driver_dynamic_dev)) {for (i = 0; i < driver->num; i++) {d = Tty_register_device (d
			River, I, NULL);
				if (Is_err (d)) {error = Ptr_err (d);
			Goto err;
	}} proc_tty_register_driver (driver);
	Driver->flags |= tty_driver_installed;

return 0;

	Err:for (i--i >= 0; i--) tty_unregister_device (driver, i);
	Mutex_lock (&tty_mutex);
	List_del (&driver->tty_drivers);

	Mutex_unlock (&tty_mutex);
	Unregister_chrdev_region (Dev, driver->num);
	Driver->ttys = NULL;
	Driver->termios = NULL;
	Kfree (P);
return error; }
You can see that the internal implementation of this function is actually registering a character device.
Look at this line: Cdev_init (&driver->cdev, &tty_fops);

From this tty_fops find the interface of the serial open function:


You can see that the open operation corresponds to the Tty_open (where the tty_fops is the file_operations of the character device)

Jump to this function to see the line the arrow points to:


This ops is actually struct tty_operations type:


Here's a summary: The open serial port of the application space calls the Tty_open in the Tty_ops, and then Tty_open calls the Uart_open function in Uart_ops, which does not involve the driver layer in the TTY hierarchy.

Next jump to uart_open this function:

static int Uart_open (struct tty_struct *tty, struct file *filp) {struct Uart_driver *drv = (struct uart_driver *) tty-&gt
	;d river->driver_state;
	struct Uart_state *state;
	struct Tty_port *port;

	int retval, line = tty->index;
	Bug_on (!tty_locked ());

	Pr_debug ("Uart_open (%d) called\n", line);
	State = Uart_get (DRV, line);
		if (is_err) {retval = Ptr_err (state);
	Goto fail;

	} port = &state->port;
	Tty->driver_data = State;
	State->uart_port->state = State; Tty->low_latency = (State->uart_port->flags & upf_low_latency)?
	1:0;
	tty->alt_speed = 0;

	Tty_port_tty_set (port, TTY);
	 /* If The port is in the middle of closing, bail.
		*/if (tty_hung_up_p (FILP)) {retval =-eagain;
		port->count--;
		Mutex_unlock (&port->mutex);
	Goto fail;
	 }/* Make sure the device was in D0 state.

	*/if (Port->count = 1) uart_change_pm (state, 0);
	 * * Start up the serial port. * * retval = Uart_startup (TTY, State, 0);
	 * * If We succeeded, wait until the ' port is ready.
	* * Mutex_unlock (&AMP;PORT-&GT;MUTEX);

if (retval = = 0) retval = Tty_port_block_til_ready (port, TTY, FILP); Fail:return retval;

You can see that this function calls a uart_startup function that is still in TTY and does not involve the serial driver layer.


This function is relatively long, intercepting the important part, as mentioned in the previous article, see the second Red Arrow refers to the part: Uport OPS Startup

Uport type can be seen from the first arrow refers to the part of the struct uart_port type, a uart_port corresponds to a serial port, in this data structure is for the serial port function operation set, and these function operation set is by serial port driver to achieve.

So now is to find the serial operation set inside the START_UP, and this will be from the drive inside to find.

Also the serial port initialization analysis can find the port in the serial initialization is obtained from the probe function:


And this array structure is defined as follows: Each port represents a serial port


Let's look at the contents of the serial drive operation set that this red arrow points to:



The above screenshot is more information, the left and right can be compared to see, one is the function pointer, one is the function of the function pointer corresponding to the name.

So here's a summary of the function call relationships of the serial open operation:

Open---> Tty_open (inside of tty_ops)---> Uart_open (uart_ops inside)---> Uart_start---> The part of the Red arrow in the image above (this is the equivalent of the O in the drive layer) Pen

Now jump to this function:


The code volume is not much compared with the Code analysis summarized the following figure:


The above is the entire serial port open operation realization.


Next to analyze the serial port driver to send operations, or the first image:


The overall parsing process is the same as the open function.

Write---> Tty_write---> N_tty_write (in line procedures)---> uart_write---> Uart_start---> Look up the fourth picture, Which is the corresponding write operation of the driver layer


Here, skip to the S3C24XX_SERIAL_START_TX function:


From the above source can see there is no action register to send the part. Here's a little trick. The key is in the ENABLE_IRQ (OURPORT->TX_IRQ) place.

An interrupt handler is available to handle sending when the interrupt is sent. Here is just an activation interrupt send handler.


In this function you can see that a send interrupt handler has been registered. Jump into this function and look inside.


You can see the Register Operations section in the above code. General Summary: The application layer of the write serial operation will eventually invoke the above S3c24xx_serial_start_tx function, and this function is only a activation of the function of sending interrupts, the specific data sending process is in the registration sent interrupt to achieve.

The following diagram is a summary of what is achieved by this function:




After the analysis is sent, the following is an analysis of the actions that receive the Read function:


function the entire call flow corresponds to the same as write.

With the above foundation in mind, here are two questions to consider:

1. The TTY subsystem responds to user requests for read data.

2. Serial port driver is how to receive processing.


is actually the same as the write operation. The following is a brief analysis:

The first node in the read function that responds to user space is the tty_read in the struct file_operations structure:


Next jump to this function to see the source code:


The Red Arrow section can see that this line is actually calling the Read,ops data type in the line code:


Let's look at the type of structure in which read is located:


In fact, this called read function corresponds to the read in the line code.

Now let's take a look at the wiring code struct TTY_LDISC_OPS Tty_ldisc_n_tty This structure:


You can see here tty_read in the line code of the N_tty_read to respond.

N_tty_read This function code is more. This is not a complete picture. Analyze only the three more important parts


The arrow refers to the part where the application is set to block the process. (This line of code is not blocked immediately)

Then there is a judgment in the second if statement below the arrow, input_available_p to determine whether there is data read.


When there is no data to read, it will block and not be CPU-scheduled to occupy the CPU. The combination above is that if there's no data, it'll block.

If there's data, it's going to read the data from the READ_BUF.


Look at the internal implementation of this function:


In fact, this read_buf and drive is closely related, when the driver has data, the driver will send the data to the read_buf inside. Let's see how the driver receives the data.

Or as a write function. In the driver module initialization there is a registration send interrupt function, and then jumps to the S3c24xx_serial_startup function

In this function, there is a REQUEST_IRQ function in which one function pointer parameter is the S3C24XX_SERIAL_RX_CHARS function.



After the analysis of the following to start the drive code ... This is only in the original code based on the analysis of the above process to achieve their own serial-driven key parts:

Here are two of the most central features:

1. Serial Driver Send interrupt Handler

2. Serial driver receive interrupt handler


Open samsung.c, find the static irqreturn_t s3c24xx_serial_tx_chars (int irq, void *id) send interrupt function, delete the code inside, and then implement it according to the analysis process above.


To send an interrupt handler code:


First step: 1. Determine if X_char is 0, if not 0, send X_char


X_char member in port structure, for Xon or Xoff (this is associated with flow control)

<span style= "FONT-SIZE:18PX;"
	>static irqreturn_t s3c24xx_serial_tx_chars (int irq, void *id) {struct s3c24xx_uart_port = ID;
	struct Uart_port *port = &ourport->port;
	
	struct Circ_buf *xmit = &port->state->xmit;//loop buffer int count = 256; 1. Determine if X_char is 0, if not 0, send X_char if (Port->x_char) {WR_REGB (port, S3c2410_utxh,  port->x_char);
	To send a character is actually to write the data into the UTXH register goto out; }//2. To determine if the send buffer is empty or if the driver is set to stop sending the IF (Uart_circ_empty (xmit)) | |
	(uart_tx_stopped (port))
		{S3C24XX_SERIAL_STOP_TX (port);
	Goto out; }//3. Circular send, cyclic condition: send buffer NOT NULL while ((!uart_circ_empty (xmit)) | | (count--) > 0) {//3.1 send FIFO if full, exit send if (RD_REGL (port, S3c2410_ufstat) & (1 << 14))//Here's to check datasheet UF
				
		Stat register 14 bit break; 3.2 The characters to be sent are written to the Send register WR_REGB (port, S3c2410_utxh, Xmit->buf[xmit->tail]);//Remove data from the tail Xmit->tail = (xmit->t AIL + 1 & (uart_xmit_size-1);/loop, if the last one starts sending the first bit//3.3 modify the trailing position of the loop buffer port->icount.tx++;//Update sent statistics}//4. If the remaining amount of data in the buffer is sent uart_circ_chars_pending<256//Then the blocked send process is awakened before Uart_write_wakeup if (uart_circ_chars_pending (XMit) <

	
	256) Uart_write_wakeup (port); 5.
		
If the send buffer is empty, the send enabled if (Uart_circ_empty (XMit)) S3c24xx_serial_stop_tx (port) is turned off; Out:return irq_handled;//function exits, indicating that interrupts have been processed}</span>
The above code in addition to the previous analysis of the flow chart and the source code, as well as datasheet, here is not a screenshot of the shot Cheung.

Then make Uimage arch=arm compele_cross=arm-linux-compile the kernel source code. Download Uimage to the Development Board to start the kernel. We'll find a little problem here. But look at the core source to see what can be solved.

Serial driver receive interrupt handler:



s3c24xx_serial_rx_chars1111 (int irq, void *dev_id) {struct S3c24xx_uart_port *ourport = dev_id;
	struct Uart_port *port = &ourport->port;
	struct Tty_struct *tty = port->state->port.tty;
	unsigned int ufcon, CH, flag, Ufstat, Uerstat; int max_count = 64;//The maximum number of characters to receive at one time (max_count--> 0) {ufcon = Rd_regl (port, S3c2410_ufcon); Read//1 register UF Stat = RD_REGL (port, S3c2410_ufstat);//2. Read the Upstat register//3.

		If the amount of data in the FIFO is 0, exit if (s3c24xx_serial_rx_fifocnt (Ourport, ufstat) = = 0) break; Uerstat = Rd_regl (port, S3c2410_uerstat);//4.  Read Uerstat Register ch = RD_REGB (port, S3C2410_URXH)//Remove character if (Port->flags & Upf_cons_flow) {//6. Stream control processing int TXE =

			S3c24xx_serial_txempty_nofifo (port);
					if (rx_enabled (port) {if (!txe) {rx_enabled (port) = 0;
				Continue
					} else {if (txe) {Ufcon |= S3c2410_ufcon_resetrx;
					WR_REGL (port, S3c2410_ufcon, Ufcon);
					rx_enabled (port) = 1;
				Goto out;
			} continue;
}
		}
		/* Insert the character into the buffer */flag = Tty_normal;

		port->icount.rx++;

			if (Unlikely (Uerstat & S3c2410_uerstat_any)) {dbg ("Rxerr:port ch=0x%02x, rxs=0x%08x\n", ch, uerstat);
				/* Check for Break */if (Uerstat & S3c2410_uerstat_break) {dbg ("break!\n");
				port->icount.brk++;
			if (Uart_handle_break (port)) goto Ignore_char;
			} if (Uerstat & S3c2410_uerstat_frame) port->icount.frame++;

			if (Uerstat & S3c2410_uerstat_overrun) port->icount.overrun++;

			Uerstat &= port->read_status_mask;
			if (Uerstat & s3c2410_uerstat_break) flag = Tty_break;
			else if (Uerstat & s3c2410_uerstat_parity) flag = tty_parity;
					    else if (Uerstat & S3c2410_uerstat_frame |
		S3c2410_uerstat_overrun)) flag = Tty_frame;
		
		} if (Uart_handle_sysrq_char (port, ch)) Goto Ignore_char; 9.
				Sends the received character to the serial port-driven buf Uart_insert_char (port, Uerstat, S3c2410_uerstat_overrun, CH, flag);
	Ignore_char:continue; }//10.

 Sending the data received by serial port to the Read_buf Tty_flip_buffer_push (TTY) of the circuit code;
Out:return irq_handled; }
Or the expression of the pear. Heavy responsibilities.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.