The Serial Driver Layer
In the last two installments of Driving Me Nuts [LJ August 2002 and October 2002], we covered the tty layer, explaining how to create a minimal tty driver. We also explained some of the various ioctls and how to interpret the termios structure. Those articles are a great start if you have to implement a new tty-type device for your embedded system, such as a serial port. For every new system that is designed, the hardware engineers always like to place the serial port at a different address, or use a different UART, or sometimes just forget the serial port and use a USB port. So most developers want to create a whole new tty driver for their new devices in order for the hardware to work properly on Linux. Fortunately, there are some layers above the tty layer that help buffer its complexities and provide the developer with a lot of common functions that are needed for a serial driver and that fit the UART or USB model better. These layers are the serial and USB to serial driver layers. We cover the serial driver layer in this article and will cover the USB to serial layer in a future article.
If you look at the code for the generic PC serial driver in the 2.2 and 2.4 kernels (available at drivers/char/serial.c), you will see a very complex driver that has numerous #ifdef lines, depending on the type of hardware you are using. This file has been copied numerous times in order to provide serial support for devices that do not fit the typical PC UART device (such as the serial_amba.c and serial_21285.c drivers). Thankfully, a number of developers, led by ARM Linux maintainer Russell King, restructured the serial driver into a generic serial core and a number of smaller, hardware-specific drivers. That code showed up in the main kernel tree sometime around the 2.5.28 release. The examples in this article are from version 2.5.35, so check that things have not changed for the kernel version you are using.
The serial layer requires your driver to do two things: register the driver itself with the serial core and then register the individual serial ports as they are found in the system (through PCI enumeration or some other sort of device discovery mechanism). To register your driver, call uart_register_driver() with a pointer to a struct uart_driver structure. This function takes the information provided in the uart_driver structure and sets up the tty layer based on this.
The fields in the uart_driver structure that the serial driver needs to provide are the following:
struct module *owner; const char *driver_name; const char *dev_name; int major; int minor; int nr; struct console *cons;
The owner field is a pointer to the module owning the serial driver. This is usually set to the macro THIS_MODULE.
The driver_name field is a pointer to the string that describes this driver, which is usually the same as the dev_name field. However, when describing the dev_name field, devfs needs to be taken into account, and the characters “%d” must be added to the end of this field if devfs is selected. This is because of the way devfs creates the device nodes. As an example, the amba.c driver sets the driver_name and dev_name fields as such:
.driver_name = "ttyAM",
#ifdef CONFIG_DEVFS_FS
.dev_name = "ttyAM%d",
#else
.dev_name = "ttyAM",
#endif
The major and minor fields specify the major number for the driver and the starting minor number, respectively.
The nr field specifies the maximum number of serial ports this driver supports.
The cons field is a pointer to the struct console structure that is used if this driver can support a serial console. If the driver does not support serial console mode, this field should be set to NULL.
Now that the serial driver has been registered with the serial driver layer, each of the serial ports needs to be registered individually with a call to uart_add_one_port(). This function takes a pointer to the original uart_driver structure that was passed to uart_register_driver and a pointer to the uart_port structure. The uart_port structure is defined as the following:
struct uart_port {
spinlock_t lock; /* port lock */
unsigned int iobase; /* in/out[bwl] */
char *membase; /* read/write[bwl] */
unsigned int irq; /* irq number */
unsigned int uartclk; /* base uart clock */
unsigned char fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
unsigned int read_status_mask;
/* driver specific */
unsigned int ignore_status_mask;
/* driver specific */
struct uart_info *info;
/* pointer to parent info */
struct uart_icount icount; /* statistics */
struct console *cons;
/* struct console, if any */
#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long sysrq; /* sysrq timeout */
#endif
unsigned int flags;
unsigned int mctrl;
/* current modem ctrl settings */
unsigned int timeout;
/* character-based timeout */
unsigned int type; /* port type */
struct uart_ops *ops;
unsigned int line; /* port index */
unsigned long mapbase; /* for ioremap */
unsigned char hub6;
/* this should be in the 8250 driver */
unsigned char unused[3];
};
A majority of these fields are used by the individual serial drivers during their operation to define how the specific port is hooked up to the processor (through the hub6, iobase, membase, mapbase and iotype variables).
One of the more interesting variables in this structure is the struct uart_ops pointer, which defines a list of functions that the serial core uses to call back into the port-specific serial driver. This structure is defined as:
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *);
void (*set_mctrl)(struct uart_port *,
unsigned int mctrl);
unsigned int (*get_mctrl)(struct uart_port *);
void (*stop_tx)(struct uart_port *,
unsigned int tty_stop);
void (*start_tx)(struct uart_port *,
unsigned int tty_start);
void (*send_xchar)(struct uart_port *, char ch);
void (*stop_rx)(struct uart_port *);
void (*enable_ms)(struct uart_port *);
void (*break_ctl)(struct uart_port *, int ctl);
int (*startup)(struct uart_port *);
void (*shutdown)(struct uart_port *);
void (*change_speed)(struct uart_port *,
unsigned int cflag,
unsigned int iflag,
unsigned int quot);
void (*pm)(struct uart_port *,
unsigned int state,
unsigned int oldstate);
int (*set_wake)(struct uart_port *,
unsigned int state);
/*
* Return a string describing the port type
*/
const char *(*type)(struct uart_port *);
/*
* Release IO and memory resources used by
* the port. This includes iounmap if necessary.
*/
void (*release_port)(struct uart_port *);
/*
* Request IO and memory resources used by the
* port. This includes iomapping the port if
* necessary.
*/
int (*request_port)(struct uart_port *);
void (*config_port)(struct uart_port *, int);
int (*verify_port)(struct uart_port *,
struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int,
unsigned long);
};
This is a very large structure, with a lot of different function pointers, looking almost as bad as the tty_driver structure.
The startup function is called once each time the open(2) call happens. It is only called after the serial core has done a lot of resource checking and is sure the port needs to be opened. The serial driver usually does some hardware-specific setup to allow the port to be used in this function.
The shutdown function is the opposite of the startup function. It is called when the port is closed and all data has stopped flowing though it. This is where the hardware is told to stop, and any resources that were allocated in the startup function should be freed.
The request_port and release_port functions are used to reserve memory and other hardware resources that are related to the serial port. The config_port function is much like request_port, but it is called when the hardware can autoprobe for any serial ports connected to it and is also responsible for doing the same hardware reservations that request_port does.
The change_speed function is called whenever the port line settings need to be modified. The values passed to this function already have been cleaned up from the original termios structure that was passed through the tty layer, making the logic in the serial driver much simpler.
There are a number of functions that are used to get and set the serial line state and port status, these are:
set_mctrl: sets a new value for the MCR UART register.
get_mctrl: gets the current MCR UART register value.
stop_tx: stops the port from sending data.
start_tx: starts the port sending data.
tx_empty: returns if the port transmitter is empty or not.
send_xchar: tells the port to send the XOFF character to the host.
stop_rx: stops receiving data.
break_ctl: sends the BREAK value over the port.
enable_ms: enables the modem status interrupts.
There are two functions related to power management issues with the serial port: pm and set_wake. If your hardware platform supports power management, use these functions to handle powering the hardware up and down.
verify_port is called to verify that the settings passed to it are valid settings for the specific serial port and is called when the user makes a TIOCSSERIAL ioctl(2) call on the port (see “The tty Layer, Part II”, LJ October 2002 for more on TIOCSSERIAL).
The serial driver layer handles many of the common serial ioctls, such as TIOCMGET, TIOCMBIS, TIOCMBIC, TIOCMSET, TIOCGSERIAL, TIOCSSERIAL, TIOCSERCONFIG, TIOCSERGETLSR, TIOCMIWAIT, TIOCGICOUNT, TIOCSERGWILD and TIOCSERSWILD. If any other ioctl is called on the serial port, it is passed down to the specific port through the ioctl callback in the uart_ops structure.
The last remaining function is type, which is used to return a string describing the type of serial port. This is used in the proc file that resides in /proc/tty/driver/ and in the initial boot message when the port is discovered.
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.
Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.
Sponsored by ActiveState
| Speed Up Your Web Site with Varnish | Jun 19, 2013 |
| Non-Linux FOSS: libnotify, OS X Style | Jun 18, 2013 |
| Containers—Not Virtual Machines—Are the Future Cloud | Jun 17, 2013 |
| Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer | Jun 12, 2013 |
| Weechat, Irssi's Little Brother | Jun 11, 2013 |
| One Tail Just Isn't Enough | Jun 07, 2013 |
- Speed Up Your Web Site with Varnish
- Containers—Not Virtual Machines—Are the Future Cloud
- Linux Systems Administrator
- Non-Linux FOSS: libnotify, OS X Style
- Lock-Free Multi-Producer Multi-Consumer Queue on Ring Buffer
- Senior Perl Developer
- Technical Support Rep
- UX Designer
- Web & UI Developer (JavaScript & j Query)
- RSS Feeds
- Reply to comment | Linux Journal
3 hours 49 min ago - Yeah, user namespaces are
5 hours 5 min ago - Cari Uang
8 hours 37 min ago - user namespaces
11 hours 30 min ago - yea
11 hours 56 min ago - One advantage with VMs
14 hours 24 min ago - about info
14 hours 58 min ago - info
14 hours 59 min ago - info
15 hours ago - info
15 hours 2 min ago




Comments
serial device driver
I require driver that transmit and recieve data in other end of the system connected Via null modem. Now I want to recieve data from the hyper terminal of that system.
Visit
Visit http://saltechnoblog.blogspot.com
link
You can find the source by entering
ftp://ftp.ssc.com/pub/lj/listings/issue104/
The file is 6331|1.txt
The link to listing 1 is
The link to listing 1 is broken.