Linux serial terminal driver - s3c6410 platform (IV)

Publisher:CuriousTravelerLatest update time:2022-06-14 Source: eefocusKeywords:linux Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Following the tty line discipline in the previous article, this article mainly explains the most important data structure of tty, tty_driver


1、tty_driver


I think the tty_driver structure is the foundation of the tty terminal device. It connects the device and the driver. Moreover, the main work of a specific tty device driver is to fill in the members of the tty_driver structure and implement the member functions. The tty_driver structure is as follows:


struct tty_driver {

int magic; /* magic number for this structure */


Represents the "magic number" given to this structure, set to TTY_DRIVER_MAGIC, initialized in the alloc_tty_driver() function.

struct kref kref; /* Reference management */reference count

struct cdev cdev;

struct module  *owner;

const char *driver_name;


Indicates the name of the driver, used in /proc/tty and sysfs

const char  *name;


Indicates the device node name of the driver

int name_base; /* offset of printed name */

int major; /* major device number */Major device number


        int minor_start; /* start of minor device number */ start of minor device number


int minor_num; /* number of *possible* devices */ possible number of devices

int num; /* number of devices allocated */


In the tty_register_driver() function, these are used to register the character device and link it with the top-level operation function mentioned above.


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,

};


The previous article has listed the source code of tty_register_driver(). For the sake of clarity, I will list it again:


/*

 * Called by a tty driver to register itself.

 */

int tty_register_driver(struct tty_driver *driver)

{

int error;

int i;

dev_t dev;

void **p = NULL;



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++)

   tty_register_device(driver, i, NULL);

}

proc_tty_register_driver(driver);

driver->flags |= TTY_DRIVER_INSTALLED;

return 0;

}


The tty_register_driver() source code ends here.


The following is still the content of the tty_driver structure:



short type; /* type of tty driver */tty driver type and subtype

short subtype; /* subtype of tty driver */

struct ktermios init_termios; /* Initial 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


        Pointer to tty data structure

*/

struct tty_struct **ttys;

struct ktermios **termios;


Used to save the current line settings, which control the current baud rate, data size, data flow control settings, etc. The driver will initialize this member with a standard value, which is copied from the tty_std_termios variable.


struct ktermios tty_std_termios = { /* for the benefit of tty drivers  */

.c_iflag = ICRNL | IXON,

.c_oflag = OPOST | ONLCR,

.c_cflag = B38400 | CS8 | CREAD | HUPCL,

.c_lflag = ISIG | ICANON | ECHO | ECHO | ECHO |

  ECHOCTL | ECHOKE | IEXTEN,

.c_cc = INIT_C_CC,

.c_ispeed = 38400,

.c_ospeed = 38400

};

struct ktermios **termios_locked;

void *driver_state;


/*

* Driver methods are operation functions in the driver. Their member functions are usually assigned in the initialization function of the specific device tty driver module.

*/


const struct tty_operations *ops;




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);

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 (*read_proc)(char *page, char **start, off_t off,

 int count, int *eof, void *data);

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 tty_struct *real_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

};

struct list_head tty_drivers;

};


2.tty device


It is not enough to have a driver, the driver must be attached to a device. tty_register_device() is used to register a tty device. Of course, there are other functions, such as the deregistration function. This is just an example.


struct device *tty_register_device(struct tty_driver *driver, unsigned index,

  struct device *device)

{

char name[64];

dev_t dev = MKDEV(driver->major, driver->minor_start) + index;



if (index >= driver->num) {

printk(KERN_ERR "Attempt to register invalid tty line number "

      " (%d).n", index);

return ERR_PTR(-SINGLE);

}



if (driver->type == TTY_DRIVER_TYPE_PTY)

pty_line_name(driver, index, name);

else

tty_line_name(driver, index, name);



return device_create(tty_class, device, dev, NULL, name);

}


Until the last function called



/**

 * device_add - add device to device hierarchy. Adding to the device tree is probably the binding of the bus, class, etc. of the sysfs file system, and building the device file on that bus, parent node, etc. The details are not clear.

 * @dev: device.

 *

 * This is part 2 of device_register(), though may be called

 * separately _iff_ device_initialize() has been called separately.

 *

 * This adds @dev to the kobject hierarchy via kobject_add(), adds it

 * to the global and sibling lists for the device, then

 * adds it to the other relevant subsystems of the driver model.

 *

 * NOTE: _Never_ directly free @dev after calling this function, even

 * if it returned an error! Always use put_device() to give up your

 * reference instead.

 */

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;

int error = -SINGLE SELECT;



dev = get_device(dev);

if (!dev)

goto done;



/* Temporarily support init_name if it is set.

* It will override bus_id for now */

if (dev->init_name)

dev_set_name(dev, "%s", dev->init_name);



if (!strlen(dev->bus_id))

goto done;



pr_debug("device: '%s': %sn", dev->bus_id, __func__);



parent = get_device(dev->parent);

setup_parent(dev, parent);



/* use parent numa_node */

if (parent)

set_dev_node(dev, dev_to_node(parent));



/* first, register with generic layer. */

error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);

if (error)

goto Error;



/* notify platform of device entry */

if (platform_notify)

platform_notify(dev);



/* notify clients of device entry (new way) */

if (dev->bus)

blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

    BUS_NOTIFY_ADD_DEVICE, dev);



error = device_create_file(dev, &uevent_attr);

if (error)

goto attrError;



if (MAJOR(dev->devt)) {

error = device_create_file(dev, &devt_attr);

if (error)

goto ueventattrError;



error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

}



error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error = device_add_attrs(dev);

if (error)

goto AttrsError;

error = bus_add_device(dev);

if (error)

goto BusError;

error = dpm_sysfs_add(dev);

if (error)

goto DPMError;

device_pm_add(dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);

bus_attach_device(dev);

if (parent)

klist_add_tail(&dev->knode_parent, &parent->klist_children);



if (dev->class) {

mutex_lock(&dev->class->p->class_mutex);

/* tie the class to the device */

klist_add_tail(&dev->knode_class,

      &dev->class->p->class_devices);



/* notify any interfaces that the device is here */

list_for_each_entry(class_intf,

   &dev->class->p->class_interfaces, node)

if (class_intf->add_dev)

[1] [2]
Keywords:linux Reference address:Linux serial terminal driver - s3c6410 platform (IV)

Previous article:Analysis of nandflash driver under Linux (3) - Based on s3c6410 platform
Next article:Linux serial terminal driver - s3c6410 platform (Part 2)

Recommended ReadingLatest update time:2024-11-16 17:55

arm-linux-gcc and simple makefile
gcc common options How to use gcc: gcc filename -v: Check the version of the gcc compiler and display the detailed process of gcc execution -o: specifies the output file name as file, which does not need to be the same as the compiled file name -E: preprocess only; do not compile, assemble or link (preprocess only, n
[Microcontroller]
Play with mini2440 development board [Compile and download the Linux kernel]
Today, let's first play with the compilation and downloading of the Linux kernel. 1. Background Development environment: 64-bit Ubuntu 14.04; Compilation tool: arm-linux-gcc 4.4.3; Download tool: SuperViVi USB Transfer Utility; Debugging tools: SecureCRT 7.2.6; Development board: Friendly Arm mini2440 (64M version);
[Microcontroller]
Play with mini2440 development board [Compile and download the Linux kernel]
Development of touch screen driver on embedded Linux platform
    introduction     Touch screens are becoming more and more popular as input devices for embedded systems by various terminal product manufacturers due to their convenience, flexibility, space saving and intuitiveness. The Linux operating system is a popular choice for embedded systems because of its advantages of o
[Microcontroller]
Detailed introduction to building a cross-compilation toolchain for ARM Linux
learning target: ● Understand the cross-compilation toolchain ● Understand the step-by-step method of building a cross-compilation tool chain ● Learn to use Crosstool to build a cross-compilation tool chain 2.1 Introduction to the cross-compilation toolchain Readers may wonder why we need a cross compiler? Cross com
[Microcontroller]
A brief analysis of the interrupt handling architecture of Linux drivers
The interrupt handling in S3C2440 is ultimately implemented through IRQ. The IRQ exception handling process has been introduced in the brief analysis of the Linux driver exception handling architecture, and finally a C function asm_do_IRQ was analyzed. Next, we will continue to analyze asm_do_IRQ, with the goal of d
[Microcontroller]
Introduction to the concepts of asynchronous or synchronous, blocking or non-blocking in Linux drivers
1. Synchronous and asynchronous Synchronous and asynchronous focus on the message communication mechanism (synchronous communication/asynchronous communication). The so-called synchronization means that when a *call* is issued, the *call* will not return before the result is obtained. But once the call retur
[Microcontroller]
Serial communication program based on QT under Linux (Tiny6410)
Since many peripherals on the ARM development board support serial communication, it is particularly important to write a serial communication program under QT. The following will describe the development process step by step: Download QT serial communication related files and examples under Linux!!! 1. Device driv
[Microcontroller]
Serial communication program based on QT under Linux (Tiny6410)
Design and implementation of handheld multimedia system based on ARM Linux QT
0 Introduction As people's living standards improve, the consumption structure has undergone tremendous changes, and the proportion of consumer spending on entertainment in total spending is constantly expanding. Handheld multimedia systems can meet people's needs for audition and portability, and can also
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号