Введение
В первых двух разделах мы представили структуру драйвера последовательного порта и основную часть tty. В этом разделе мы представляем часть драйвера последовательного порта, которая тесно связана с аппаратным обеспечением.
Драйвер UART частично зависит от аппаратной платформы, тогда как драйвер TTY не имеет никакого отношения к конкретной платформе. Хотя часть UART зависит от платформы, какой бы аппаратной она ни была, идея драйвера одна и та же, и она будет представлена отдельно в модулях ниже.
struct uart_driver
Сама структура не содержит в себе лежащего в основеUARTКак работать с оборудованием,Чтода Абстракция всех драйверов последовательных устройствиинкапсуляция。Играет роль подключения драйверов аппаратных устройств.иTTYРоль водителя。Зарегистрированstruct uart_driver
нельзя использовать послеUARTоборудование,Также необходимо связать конкретное устройство UART.
uart_driver
Представление структуры UART водить машину, это определяетсуществоватьinclude/linux/serial_core.h
в файле,Содержание следующее:
struct uart_driver {
struct module *owner;
const char *driver_name;
const char *dev_name;
int major;
int minor;
int nr;
struct console *cons;
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
owner
:Указатель на модуль владельца этого драйвера,То есть загружается модуль ядра, загружающий драйвер.driver_name
:нить,выразить имя водителя.dev_name
:нить,выразить имя устройства,То есть имя файла устройства, управляемого драйвером,Такие как ttyS.major
:выражатьоборудованиевладелец файлаоборудование Число。minor
:выражатьоборудованиевремя файлаоборудование Число。nr
:целое число,выразить Количество устройств, которыми управляет этот драйвер.cons
:ориентированный struct console
указатель типа,выражать Долженпоследовательный портоборудованиеграницаконсоль。Кроме того, структура также содержит два частных поля указателя:
state
:ориентированный struct uart_state
Указатель типа выражает информацию о внутреннем состоянии драйвера.tty_driver
:ориентированный struct tty_driver
Указатель на тип, которому соответствует драйвер tty водитель.Часто на микросхеме последовательного порта имеется несколько последовательных портов (последовательный порт). ports,Соответствует физическому последовательному порту),Эти последовательные порты имеют одинаковый механизм работы.。LinuxЯдро использует эти последовательные порты какstruct uart_port
Описание структуры。struct uart_port
используется для описанияUARTпрерывание порта、Адрес памяти ввода-вывода、Размер ФИФО、Тип порта и другая информация.
существовать Linux В ядре каждое устройство последовательного порта соответствует struct uart_port
Структура данных, и эта структура данных будет сохранена как атрибут последовательного устройства, существующего в соответствующем узле устройства середина.
Когда приложение обращается к последовательному устройству путем открытия узла устройства, ядро получает соответствующий struct uart_port
структуру данных, а затем использовать эту структуру данных для выполнения таких операций, как чтение и запись последовательного порта.
struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* in/out[bwl] */
unsigned char __iomem *membase; /* read/write[bwl] */
unsigned int (*serial_in)(struct uart_port *, int);
void (*serial_out)(struct uart_port *, int, int);
void (*set_termios)(struct uart_port *,
struct ktermios *new,
struct ktermios *old);
void (*set_mctrl)(struct uart_port *, unsigned int);
int (*startup)(struct uart_port *port);
void (*shutdown)(struct uart_port *port);
void (*throttle)(struct uart_port *port);
void (*unthrottle)(struct uart_port *port);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int old);
void (*handle_break)(struct uart_port *);
int (*rs485_config)(struct uart_port *,
struct serial_rs485 *rs485);
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io access style */
unsigned char unused1;
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_state *state; /* pointer to parent state */
struct uart_icount icount; /* statistics */
struct console *cons; /* struct console, if any */
/* flags must be updated while holding port mutex */
upf_t flags;
/*
* Must hold termios_rwsem, port mutex and port lock to change;
* can hold any one lock to read.
*/
upstat_t status;
int hw_stopped; /* sw-assisted CTS flow state */
unsigned int mctrl; /* current modem ctrl settings */
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
const struct uart_ops *ops;
unsigned int custom_divisor;
unsigned int line; /* port index */
unsigned int minor;
resource_size_t mapbase; /* for ioremap */
resource_size_t mapsize;
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char irq_wake;
unsigned char unused[2];
struct attribute_group *attr_group; /* port specific attributes */
const struct attribute_group **tty_groups; /* all attributes (serial core use only) */
struct serial_rs485 rs485;
void *private_data; /* generic platform data pointer */
};
unsigned long iobase
: Указывает базовый адрес существующего пространства ввода-вывода устройства последовательного порта.unsigned char __iomem *membase
: Указывает на адрес, отображенный в памяти последовательного устройства.unsigned int (*serial_in)(struct uart_port *, int)
: Указатель функции, используемый для чтения данных с последовательного устройства.void (*serial_out)(struct uart_port *, int, int)
: Указатель функции,используется для向последовательный портоборудованиесередина Писать Введите данные。void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old)
: Указатель функции, используемый для установки устройства последовательного порта.Параметры терминала。void (*set_mctrl)(struct uart_port *, unsigned int)
: Указатель функции, используемый для установки устройства последовательного порта. modem управляющий сигнал.int (*startup)(struct uart_port *port)
: Указатель функции,используется для初始化последовательный портоборудованиеизапускатьпередача инфекции。void (*shutdown)(struct uart_port *port)
: Указатель функции, используемый для закрытия устройства последовательного порта.void (*throttle)(struct uart_port *port)
: Указатель функции, используемый для управления потоком передачи последовательного устройства в остановленное состояние.void (*unthrottle)(struct uart_port *port)
: Указатель функции, используемый для отмены состояния остановки управления потоком передачи устройства последовательного порта.int (*handle_irq)(struct uart_port *)
: Указатель функции, используемый для обработки прерываний устройств последовательного порта.void (*pm)(struct uart_port *, unsigned int state, unsigned int old)
: Указатель функции, используемый для управления питанием последовательных устройств.void (*handle_break)(struct uart_port *)
: Указатель функции, используемый для обработки символа прерывания сигнала прерывания устройства последовательного порта.int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485)
: Указатель функции для конфигурации RS485 Параметры последовательной связи.unsigned int irq
: Номер прерывания, используемый этим устройством последовательного порта.unsigned long irqflags
: Флаг прерывания этого устройства последовательного порта.unsigned int uartclk
: Тактовая частота этого последовательного устройства.unsigned int fifosize
: Устройство последовательного порта FIFO размер.unsigned char x_char
: XON/XOFF характер.unsigned char regshift
: Зарегистрируйте смещение.unsigned char iotype
: I/O Тип доступа.unsigned char unused1
: Неиспользуемая переменная-член.unsigned int read_status_mask
: Бит маски, используемый для указания статуса чтения.unsigned int ignore_status_mask
: Бит маски, используемый для указания статуса игнорирования.struct uart_state *state
: Указатель на структуру состояния устройства последовательного порта.struct uart_icount icount
: Используется для хранения статистической информации о последовательном устройстве.struct console *cons
: Указатель на консольное устройство, которому принадлежит это последовательное устройство.unsigned int mctrl
:Текущее управление модемом(Modem Контроль) настройки. Это значение содержит статус текущего сигнала управления (например, DTR, RTS, DSR, CTS и т. д.). Обычно управляется аппаратно.unsigned int timeout
:Тайм-аут на основе символов。Когда персонажпередача инфекцииприезжатьUARTпорт,Если следующий символ не получен в течение указанного времени,Истечет время ожидания и будет отправлено уведомление. Обычно устанавливается водителем.unsigned int type
:Тип порта。Это значение обычно используется для идентификацииUARTОсобые свойства фурнитуры(Например, тип чипа、Диапазон скорости передачи данных и т. д.).const struct uart_ops *ops
:одинориентированныйstruct структура uart_opsизуказатель。Эта структура содержитUARTСвязанные с драйвером Указатель функции,нравитьсяUARTчитать、Писать、запускать、Хватит ждать ожидания.unsigned int custom_divisor
:Пользовательский делитель,Используется для реализации нестандартных скоростей передачи данных. Это значение обычно задается драйвером.unsigned int line
:индекс порта,Номер, используемый для идентификации этого порта UART.unsigned int minor
:портиз次оборудование Число,Используется для определения местоположения порта UART в системе.resource_size_t mapbase、resource_size_t mapsize
:Начальный адрес отображаемой областииразмер.Эти значения обычно задаются драйвером,Используется для сопоставления физического адреса порта UART с виртуальным адресом.struct device *dev
:ориентированныйотецоборудованиеизуказатель。в целомда ДолженUARTоборудование所соединятьиз总线控制器оборудование。unsigned char hub6
:используется для обозначенияHub6Статус совета。Эта переменная должна Должендасуществовать8250определено в драйвере。unsigned char suspended
:используется для обозначения Долженпортда Будет ли приостановлено。unsigned char irq_wake
:используется для обозначения Долженпортда Поддерживать ли прерывание пробуждения。unsigned char unused[2]
:неиспользованные байты。struct attribute_group *attr_group
:ориентированный属性组изуказатель。Группа атрибутов содержитUARTоборудованиеиз属性идействовать,Например, состояние устройства, настройки скорости передачи данных и т. д. ожидание.const struct attribute_group **tty_groups
:ориентированныйуказатель数组изуказатель,Этот массив содержит указатели на все группы атрибутов.,Для использования последовательным ядром.struct serial_rs485 rs485
:RS485Структура конфигурации,Используется для связи RS485.void *private_data
:ориентированный私有数据изуказатель。этотуказательв целомводителемиспользовать,Используется для сохранения данных, относящихся к драйверу.Linux В конечном итоге система вызывает систему для отправки и получения данных. ops функция в . ops да uart_ops
类型из Структурауказательпеременная。uartаппаратное обеспечениедействоватьфункциясобирать,Базовый драйвер оборудования должен реализовывать эту структуру.
uart_ops
Структура Используется для определения интерфейса для драйвера последовательного порта, позволяя верхнему уровню вызывать эти интерфейсы для реализации таких операций, как чтение и запись последовательного порта. Он содержит множество указателей функций, каждый указатель функции соответствует определенной операции последовательного порта.
В ядре Linux драйвер последовательного порта разделен на два уровня: драйвер чипа последовательного порта и serial core
слой。Чтосередина,serial core
Уровень предоставляет большое количество функциональных интерфейсов для использования драйвером микросхемы последовательного порта верхнего уровня. Определения этих функциональных интерфейсов включают существование. struct uart_ops
в структуре.
При написании драйвера микросхемы последовательного порта необходимо реализовать struct uart_ops
Каждый функциональный интерфейс определен в структуре так, что serial core
вызов слоя.
Например, существование реализовано в драйвере микросхемы uart_start()
Функция соответствует struct uart_ops
в структуре startup
указатель функции.
поэтому,struct uart_ops
Структура да является ключом к реализации драйвера последовательного порта. Она определяет все функциональные интерфейсы, которые должен реализовать драйвер, и связана с ними. serial core
слои связаны.
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 *);
void (*start_tx)(struct uart_port *);
void (*throttle)(struct uart_port *);
void (*unthrottle)(struct uart_port *);
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 (*flush_buffer)(struct uart_port *);
void (*set_termios)(struct uart_port *, struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *, struct ktermios *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
void (*wake_peer)(struct uart_port *);
/*
* Return a string describing the type of the port
*/
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);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
tx_empty()
:Проверьте буфер отправки последовательного портада Нет пусто,Используется, чтобы определить, может ли да отправлять данные.set_mctrl()
:Настройте последовательный порт modem управляющие сигналы, такие как RTS、DTR ждать.get_mctrl()
:Получить последовательный порт modem управляющий сигнал.stop_tx()
:Остановить токсуществоватьданные отправлены。start_tx()
:Начать отправку данных。throttle()
:Ограничить скорость отправки,Уменьшите объем отправляемых данных.unthrottle()
:Отмена Ограничить скорость отправки。send_xchar()
:отправить XON или XOFF Символ, используемый для управления потоком.stop_rx()
:Прекратить получение данных。enable_ms()
:включить последовательный порт modem Функция определения статуса.break_ctl()
:отправить break Сигнал.startup()
:Инициализация оборудования последовательного порта。shutdown()
:Закройте оборудование последовательного порта。flush_buffer()
:Очистить буфер последовательного порта。set_termios()
:Настройте последовательный порт Параметры терминала。set_ldisc()
:Настройте последовательный порт Правила отраслино。pm()
:Реализовать последовательный порт power management。wake_peer()
:используется длябудить Что他休眠状态изпоследовательный порт。Кроме того, включены некоторые указатели функций для обработки ресурсов ввода-вывода последовательного порта:
type()
:返回描述последовательный порт类型изнить。release_port()
:释放последовательный портиз IO и ресурсы памяти,включая подъем IO картографированиеждать.request_port()
:请求последовательный портиз IO и ресурсы памяти,включая IO картографированиеждать.config_port()
:Настройка параметров последовательного порта。verify_port()
:Проверьте параметры последовательного портадаправильно или нет。ioctl()
:实现последовательный портоборудованиеиз ioctl интерфейс.uart_state
выражать UART статус и с struct uart_port
Структуры используются вместе для управления UART порт.
struct uart_port
Представление структуры UART информацию об аппаратном обеспечении и операциях порта, в то время как struct uart_state
Структура содержит статус программного обеспечения, связанный с портом.
потому что UART Статус может содержать несколько состояний, поэтому несколько состояний можно использовать одновременно. UART статус для управления несколькими UART Портовые операции.
struct uart_state {
struct tty_port port;
enum uart_pm_state pm_state;
struct circ_buf xmit;
struct uart_port *uart_port;
};
struct tty_port port
:выражать tty Информация о статусе порта,Включает буферы приема и отправки.,Информация управления и информация управления потоком и т. д. ожидание.enum uart_pm_state pm_state
:выражатьпоследовательный портоборудованиеиз电源管理状态,Можетда UART_PM_STATE_ON
、UART_PM_STATE_OFF
или UART_PM_STATE_UNDEFINED
。struct circ_buf xmit
:выражатьпоследовательный портоборудованиеиз发送缓冲区,Используется для хранения данных для отправки.struct uart_port *uart_port
:выражать Долженпоследовательный порт Устройство соответствующее struct uart_port
Структура.Когда приложение записывает данные на последовательное устройство, данные будут храниться в xmit
буфер и запускает функцию обработки отправки данных драйвера последовательного порта. Эта функция запустится с xmit
Возьмите данные из буфера и передайте uart_port
Указатель функции отправляет данные на физический последовательный порт. существуют При отправке данных драйвер также будет в соответствии с Состояние управления потоком последовательного порта выполняет управление потоком данных.
Когда данные будут получены, они будут сохранены в port
в буфере приема и запускает функцию обработки приема данных драйвера последовательного порта. Функция-обработчик возьмет данные из буфера приема и передаст их приложению.
После абстрагирования структуры данных последовательное ядро предоставляет удобный программный API базовому драйверу, в основном включающий следующие функции.
uart_register_driver
будет определен и заполненuart driver
Зарегистрируйтесь, чтобыkernelсередина,Обычно существование вызывается в интерфейсе инициализации модуля драйвера.
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
BUG_ON(drv->state);
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
drv->tty_driver = normal;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start = drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype = SERIAL_TYPE_NORMAL;
normal->init_termios = tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state = drv;
tty_set_operations(normal, &uart_ops);
/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) {
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;
tty_port_init(port);
port->ops = &uart_port_ops;
}
retval = tty_register_driver(normal);
if (retval >= 0)
return retval;
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
uart_register_driver()
Зарегистрируйте свою работунравиться Вниз:
uart_state
пространство, каждый uart_state
У каждого есть один uart_port 。tty_driver
объект и инициализировать его различные свойства, такие как driver_name
,name
,major
,minor_start
и т. д. Эти атрибуты да используются для существования. TTY Создано в подсистеме tty устройство, их значения берутся из uart_driver
Значение, указанное в объекте.tty_driver
Средние настройки tty операция, среди которых tty_ops
даа структура, которая определяет UART Функция, необходимая для последовательного интерфейса. После того, как эти функциисуществовать устройства последовательного порта зарегистрированы, когда есть данные, входящие и исходящие через последовательный порт, TTY Подсистема будет вызывать ихфункция。tty_set_operations()
функцияиспользуется длясуществовать tty_driver
Средние настройки tty действовать.tty_driver
Наконец, функция пройдет все UART Объекты состояния устройства и инициализировать их. Эти объекты состояния хранятся существующим uart_driver
Объект state
в поле. каждый UART Объект состояния устройства содержит tty_port
Объект, в котором хранится информация об Устройстве последовательного информация о порте, такая как управление потоком, длина слова, четность и т. д. ждать.существовать здесь, tty_port
Операция настроена на uart_port_ops
,Он содержит конкретную реализацию UART Функция, необходимая для последовательного интерфейса.tty_register_driver()
Функция регистрации в ядре tty водитель и поместите водительское tty_driver
Структура и uart_driver
Связано со структурой.Подвести итогслово:tty serial core
базовый уровень драйвераиttyСоединение между слоями должно начинаться сuart_register_driver()
серединасоединять,tty_driver
дасуществоватьuart_driver
Процесс регистрациисередина构建из。
uart_unregister_driver
даодинLinuxЯдров Отмена регистрации серийного драйверафункция,Используется для отключения ранее зарегистрированного драйвера от устройства последовательного порта системы.
/**
* uart_unregister_driver - remove a driver from the uart core layer
* @drv: low level driver structure
*
* Remove all references to a driver from the core driver. The low
* level driver must have removed all its ports via the
* uart_remove_one_port() if it registered them with uart_add_one_port().
* (ie, drv->port == NULL)
*/
void uart_unregister_driver(struct uart_driver *drv)
{
struct tty_driver *p = drv->tty_driver;
unsigned int i;
/*Получаем информацию, связанную с драйвером tty_driver Пример*/
tty_unregister_driver(p);
/*Отменяем регистрацию драйвера и подключаем его к системе. tty Отключение устройства*/
put_tty_driver(p);
/*Выпускаем tty_driver Например, если счетчик использования экземпляра в этот момент равен нулю, то есть ни один другой модуль существования не использует этот экземпляр, то он будет полностью выгружен и все ресурсы памяти будут освобождены*/
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
kfree(drv->state);
drv->state = NULL;
drv->tty_driver = NULL;
}
uart_add_one_port
используется для ВоляодинUARTпорт добавлен вUART驱动程序из状态表середина,И зарегистрируйте устройство порта TTY.,Позволяет пользовательскому пространству обмениваться данными с UART через это устройство.
/**
* uart_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure to use for this port.
*
* This allows the driver to register its own uart_port structure
* with the core driver. The main purpose is to allow the low
* level uart drivers to expand uart_port, rather than having yet
* more levels of structures.
*/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
int num_groups;
/*Проверяем, находится ли да в контексте прерывания, если да напрямую возвращает ошибку*/
BUG_ON(in_interrupt());
/*Проверяем, находится ли добавленный порт да за пределами диапазона, поддерживаемого драйвером, если да, возвращаем EINVAL*/
if (uport->line >= drv->nr)
return -EINVAL;
/*Получаем информацию о состоянии (uart_state) и порт (tty_port), соответствующий порту*/
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
/*Проверяем, занят ли порт да другими устройствами, если да, возвращаем EINVAL*/
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
/* Свяжите порт и таблицу состояния драйвера и выполните соответствующие работы по инициализации, включая статус PM, консоль, спин-блокировку и т. д. */
state->uart_port = uport;
uport->state = state;
state->pm_state = UART_PM_STATE_UNDEFINED;
uport->cons = drv->cons;
uport->minor = drv->tty_driver->minor_start + uport->line;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
if (uport->cons && uport->dev)
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
/*Настройте атрибуты порта, такие как скорость передачи данных, биты данных, стоповые биты и т. д.*/
uart_configure_port(drv, state, uport);
num_groups = 2;
if (uport->attr_group)
num_groups++;
/*Назначаем и устанавливаем группы атрибутов устройства TTY, которые включают в себя общие группы атрибутов устройства TTY и определяемые пользователем группы атрибутов*/
uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
GFP_KERNEL);
if (!uport->tty_groups) {
ret = -ENOMEM;
goto out;
}
uport->tty_groups[0] = &tty_dev_attr_group;
if (uport->attr_group)
uport->tty_groups[1] = uport->attr_group;
/*Зарегистрируем устройство порта TTY и свяжем его с tty_driverиtty_port*/
tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
uport->line, uport->dev, port, uport->tty_groups);
/*Если регистрация прошла успешно, помечаем устройство как пробуждаемое*/
if (likely(!IS_ERR(tty_dev))) {
device_set_wakeup_capable(tty_dev, 1);
} else {
dev_err(uport->dev, "Cannot register tty device on line %d\n",
uport->line);
}
/*
* Ensure UPF_DEAD is not set.
*/
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
uart_remove_one_port
используется дляиз основного драйверасерединаразделение(отключиться)один指定изпортструктура。
/**
* uart_remove_one_port - detach a driver defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure for this port
*
* This unhooks (and hangs up) the specified port structure from the
* core driver. No further calls will be made to the low-level code
* for this port.
*/
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct tty_struct *tty;
int ret = 0;
/*Проверяем, находится ли текущий да в контексте прерывания*/
BUG_ON(in_interrupt());
/*Проверяем, равен ли указатель порта uart в структуре состояния uart указателю порта uart, переданному в функцию, если нет, выводим сообщение об ошибке*/
if (state->uart_port != uport)
dev_alert(uport->dev, "Removing wrong port: %p != %p\n",
state->uart_port, uport);
/*Получаем блокировку мьютекса структуры порта tty, которая используется для предотвращения одновременного изменения состояния порта*/
mutex_lock(&port_mutex);
/*Получаем блокировку мьютекса структуры порта tty, а затем проверяем, пуст ли указатель порта uart. Если пусто, выразите, что текущий порт уже занят. удалитьсуществовать. В этом случае будет возвращено -EINVAL и мьютекс будет разблокирован. */
mutex_lock(&port->mutex);
if (!state->uart_port) {
mutex_unlock(&port->mutex);
ret = -EINVAL;
goto out;
}
/*блокировка port->mutex блокировка мьютекса и будет uport->flags установлен на UPF_DEAD, выражать Порт закрыт. Разблокировано позже port->mutex。*/
uport->flags |= UPF_DEAD;
mutex_unlock(&port->mutex);
/*Удалить устройство из уровня tty*/
tty_unregister_device(drv->tty_driver, uport->line);
/*Получать tty Устройство соответствующее tty структура и использование tty_vhangup() Функция Закрыть это tty Терминал управления устройством. Наконец, используйте tty_kref_put() Функциональный выпуск tty Счетчик ссылок на структуру. */
tty = tty_port_tty_get(port);
if (tty) {
tty_vhangup(port->tty);
tty_kref_put(tty);
}
/*Если этот порт используется как консольный, используйте unregister_console() Функция Отменить консольную регистрацию этого порта*/
if (uart_console(uport))
unregister_console(uport->cons);
/*в соответствии с uport->type значение для освобождения порта IO и ресурсы памяти, если uport->type Значение PORT_UNKNOWN, тогда выражение не имеет соответствующих ресурсов, которые необходимо освободить*/
if (uport->type != PORT_UNKNOWN)
uport->ops->release_port(uport);
kfree(uport->tty_groups);
/*Воля uport->type ценитьустановлен на PORT_UNKNOWN, выражать Порт больше не существует. В то же время, state->uart_port установлен на NULL,выражать state Соответствующий порт больше не связан с uport связанный. */
uport->type = PORT_UNKNOWN;
state->uart_port = NULL;
out:
mutex_unlock(&port_mutex);
return ret;
}
uart_write_wakeup
uart_write_wakeupПробуждение последовательного порта верхнего уровня Писатьпроцесс заблокирован данными,Обычно существует функция обработчика прерывания отправки последовательного порта.
/*
* This routine is used by the interrupt handler to schedule processing in
* the software interrupt portion of the driver.
*/
void uart_write_wakeup(struct uart_port *port)
{
struct uart_state *state = port->state;
/*
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
BUG_ON(!state);
/*функцияпросыпаться сstate->portсвязанный терминал。*/
tty_wakeup(state->port.tty);
}
uart_suspend_portфункция используется для приостановки работы порта для управления питанием. он выполняет ряд операций,В том числе проверка, может ли субустройство да разбудить систему,Прекратить отправку и получение данных,Подождите, пока буфер отправки станет пустым.,закрыть порт,Остановить консоль,и измените состояние управления питанием порта.
int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct device *tty_dev;
struct uart_match match = {uport, drv};
/*Блокируем порт, чтобы гарантировать, что условия гонки не возникнут при выполнении других операций*/
mutex_lock(&port->mutex);
/*Найти с помощьюuport->dev相关联из子оборудование。он используетmatchСтруктураиserial_match_portфункциячтобы соответствовать субоборудование*/
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
/*нравитьсяфрукты找приезжать了子оборудованиеии Долженоборудование Можетбудить系统,тогда будетuport->irqустановлен набудитьсерединаперерыв,и будетuport->irq_wakeустановлен на1. Затем,Отпустите tty_dev и разблокируйте мьютекс порта.,и возвращает 0*/
if (device_may_wakeup(tty_dev)) {
if (!enable_irq_wake(uport->irq))
uport->irq_wake = 1;
put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
}
/*Если подустройство найдено, но оно не может разбудить систему, отпустите tty_dev*/
put_device(tty_dev);
/* Nothing to do if the console is not suspending */
/*Если консоль не включает приостановку и обновление консоли, перейдите к разблокировке*/
if (!console_suspend_enabled && uart_console(uport))
goto unlock;
/*Воляuport->suspendedустановлен на1,выразиться порт завис. */
uport->suspended = 1;
/* Если порт был инициализирован, выполните некоторые операции, чтобы остановить передачу и закрыть порт. Эти операции включают установку ASYNCB_SUSPENDED и очистку флага ASYNCB_INITIALIZED, остановку отправки и получения данных, ожидание опустошения буфера отправки и закрытие порта. */
if (port->flags & ASYNC_INITIALIZED) {
const struct uart_ops *ops = uport->ops;
int tries;
set_bit(ASYNCB_SUSPENDED, &port->flags);
clear_bit(ASYNCB_INITIALIZED, &port->flags);
spin_lock_irq(&uport->lock);
ops->stop_tx(uport);
ops->set_mctrl(uport, 0);
ops->stop_rx(uport);
spin_unlock_irq(&uport->lock);
/*
* Wait for the transmitter to empty.
*/
for (tries = 3; !ops->tx_empty(uport) && tries; tries--)
msleep(10);
if (!tries)
dev_err(uport->dev, "%s%d: Unable to drain transmitter\n",
drv->dev_name,
drv->tty_driver->name_base + uport->line);
ops->shutdown(uport);
}
/*
* Disable the console device before suspending.
*/
/* */
/*Если консоль отключена, остановите консоль*/
if (uart_console(uport))
console_stop(uport->cons);
/*Вызовите функцию uart_change_pm, чтобы изменить состояние управления питанием порта на UART_PM_STATE_OFF*/
uart_change_pm(state, UART_PM_STATE_OFF);
unlock:
mutex_unlock(&port->mutex);
return 0;
}
uart_resume_port
эффектдавосстанавливатьсяодинужевешатьизUARTпорт.
int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state = drv->state + uport->line;
struct tty_port *port = &state->port;
struct device *tty_dev;
struct uart_match match = {uport, drv};
struct ktermios termios;
mutex_lock(&port->mutex);
/*Используем device_find_child для поиска структуры с именем match uart_matchсоответствиеuport->devиз子оборудование*/
tty_dev = device_find_child(uport->dev, &match, serial_match_port);
/*Если устройство найдено, порт не приостановлен и устройство может проснуться, функция отключает пробуждение по IRQ и возвращает 0*/
if (!uport->suspended && device_may_wakeup(tty_dev)) {
if (uport->irq_wake) {
disable_irq_wake(uport->irq);
uport->irq_wake = 0;
}
put_device(tty_dev);
mutex_unlock(&port->mutex);
return 0;
}
/*функция Воляuport->suspendedустановлен на0*/
put_device(tty_dev);
uport->suspended = 0;
/*
* Re-enable the console device after suspending.
*/
/*нравитьсяфруктыпортдаконсольпорт,нофункция Воляtermiosструктураустановлен настройки флага консоли*/
if (uart_console(uport)) {
/*
* First try to use the console cflag setting.
*/
memset(&termios, 0, sizeof(struct ktermios));
termios.c_cflag = uport->cons->cflag;
/*
* If that's unset, use the tty termios setting.
*/
if (port->tty && termios.c_cflag == 0)
termios = port->tty->termios;
/*нравитьсяфрукты启用了консольвешать,Затем функция использует uart_change_pm, чтобы включить состояние управления питанием.,использоватьuport->ops->set_termiosнастраиватьtermios,ииспользоватьconsole_startзапускатьконсоль*/
if (console_suspend_enabled)
uart_change_pm(state, UART_PM_STATE_ON);
uport->ops->set_termios(uport, &termios, NULL);
if (console_suspend_enabled)
console_start(uport->cons);
}
if (port->flags & ASYNC_SUSPENDED) {
const struct uart_ops *ops = uport->ops;
int ret;
/* Если порт завис, функция использует uart_change_pm для изменения состояния управления питанием на открытие */
uart_change_pm(state, UART_PM_STATE_ON);
spin_lock_irq(&uport->lock);
/*использоватьops->set_mctrlВолялиния управления модемомустановлен на0*/
ops->set_mctrl(uport, 0);
spin_unlock_irq(&uport->lock);
if (console_suspend_enabled || !uart_console(uport)) {
/* Protected by port mutex for now */
struct tty_struct *tty = port->tty;
/*использоватьops->startupзапускатьпорт*/
ret = ops->startup(uport);
if (ret == 0) {
/*нравитьсяфруктыпортуспехзапускать,Затем используйте uart_change_speed, чтобы изменить скорость порта.,использоватьops->start_txзапускатьпередача инфекции,исуществоватьport->flagsСредние настройкиASYNCB_INITIALIZEDКусочек*/ if (tty)
uart_change_speed(tty, state, NULL);
spin_lock_irq(&uport->lock);
ops->set_mctrl(uport, uport->mctrl);
ops->start_tx(uport);
spin_unlock_irq(&uport->lock);
set_bit(ASYNCB_INITIALIZED, &port->flags);
} else {
/*
* Failed to resume - maybe hardware went away?
* Clear the "initialized" flag so we won't try
* to call the low level drivers shutdown method.
*/
/*Если порт невозможно восстановить, функция очищает бит ASYNCB_INITIALIZED и вызывает uart_shutdown*/
uart_shutdown(tty, state);
}
}
clear_bit(ASYNCB_SUSPENDED, &port->flags);
}
mutex_unlock(&port->mutex);
return 0;
}
uart_get_baud_rate
,Роль функции da соответствует заданным настройкам терминала и области действия.,Получите доступную скорость передачи данных. Если вы не можете получить скорость передачи данных, отвечающую требованиям,Будет использоваться ближайшая возможная скорость передачи данных.
/**
* uart_get_baud_rate - return baud rate for a particular port
* @port: uart_port structure describing the port in question.
* @termios: desired termios settings.
* @old: old termios (or NULL)
* @min: minimum acceptable baud rate
* @max: maximum acceptable baud rate
*
* Decode the termios structure into a numeric baud rate,
* taking account of the magic 38400 baud rate (with spd_*
* flags), and mapping the %B0 rate to 9600 baud.
*
* If the new baud rate is invalid, try the old termios setting.
* If it's still invalid, we try 9600 baud.
*
* Update the @termios structure to reflect the baud rate
* we're actually going to be using. Don't do this for the case
* where B0 is requested ("hang up").
*/
unsigned int
uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
struct ktermios *old, unsigned int min, unsigned int max)
{
unsigned int try;
unsigned int baud;
unsigned int altbaud;
int hung_up = 0;
upf_t flags = port->flags & UPF_SPD_MASK;
switch (flags) {
case UPF_SPD_HI:
altbaud = 57600;
break;
case UPF_SPD_VHI:
altbaud = 115200;
break;
case UPF_SPD_SHI:
altbaud = 230400;
break;
case UPF_SPD_WARP:
altbaud = 460800;
break;
default:
altbaud = 38400;
break;
}
for (try = 0; try < 2; try++) {
baud = tty_termios_baud_rate(termios);
/*
* The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
* Die! Die! Die!
*/
if (try == 0 && baud == 38400)
baud = altbaud;
/*
* Special case: B0 rate.
*/
if (baud == 0) {
hung_up = 1;
baud = 9600;
}
if (baud >= min && baud <= max)
return baud;
/*
* Oops, the quotient was zero. Try again with
* the old baud rate if possible.
*/
termios->c_cflag &= ~CBAUD;
if (old) {
baud = tty_termios_baud_rate(old);
if (!hung_up)
tty_termios_encode_baud_rate(termios,
baud, baud);
old = NULL;
continue;
}
/*
* As a last resort, if the range cannot be met then clip to
* the nearest chip supported rate.
*/
if (!hung_up) {
if (baud <= min)
tty_termios_encode_baud_rate(termios,
min + 1, min + 1);
else
tty_termios_encode_baud_rate(termios,
max - 1, max - 1);
}
}
/* Should never happen */
WARN_ON(1);
return 0;
}
Эта функция делает следующее
UPF_SPD_MASK
Бит флага анализирует альтернативную скорость передачи данных. altbaud
。termios
анализирует текущую скорость передачи данных, если она равна 38400
,тогда будетскорость передачи данныхустановлен Альтернативная скорость передачи данных altbaud
。нравитьсяфруктыскорость передачи данных等于 0
(то есть запрос“вешать”),нонастраиватьскорость передачи данных为 9600
。нравитьсяфруктыскорость передачи данныхсуществовать min
и max
В пределах диапазона возвращается скорость передачи данных.0
,тогда функция попытается использовать старые настройки терминала.min
,нонастраиватьскорость передачи данных为 min + 1
,нетнонастраиватьскорость передачи данных为 max - 1
。uart_get_divisor
,используется для Рассчитать данныепортиз UART Значение делителя тактовой частоты для достижения заданной скорости передачи данных . Основное да рассчитывается с помощью некоторых основных математических операций. UART Значение делителя часов. Это значение да используется для настройки UART аппаратное обеспечение для достижения указанной скорости передачи данных.
существоватьпоследовательный порт通信середина,Значение делителя тактовой частоты соответствует скорости передачи данных.,То есть, чем меньше значение делителя тактовой частоты, тем,Чем выше скорость передачи данных,Чем выше скорость передачи.
/**
* uart_get_divisor - return uart clock divisor
* @port: uart_port structure describing the port.
* @baud: desired baud rate
*
* Calculate the uart clock divisor for the port.
*/
unsigned int
uart_get_divisor(struct uart_port *port, unsigned int baud)
{
unsigned int quot;
/*
* Old custom speed handling.
*/
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
else
quot = DIV_ROUND_CLOSEST(port->uartclk, 16 * baud);
return quot;
}
Эта функция делает следующее
period
,Продолжительность этого периода проходит 16 * baud
рассчитано. Затем измените порт UART тактовая частота, разделенная на period
,полученное частное quot
Как раз то, что нужно UART Значение делителя часов. используется здесь DIV_ROUND_CLOSEST
Макрос, округляющий числа с плавающей запятой до ближайшего целого значения.38400
,иипортиз标志Кусочек Значение UPF_SPD_CUST
,вам нужно использовать значение пользовательского делителя порта,Вместо значения, рассчитанного по формуле дав. Это связано с тем, что существуют некоторые старые драйверы последовательного порта.,Некоторые специальные скорости передачи данных могут поддерживаться с использованием пользовательских значений делителя.uart_update_timeout
используется для Настройте последовательный порт FIFO период тайм-аута. FIFO (первым пришел — первым вышел) — это распространенная структура, используемая для кэширования данных в оборудовании последовательного порта. Она может повысить эффективность передачи через последовательный порт. Период ожидания да относится к существованию FIFO Если передача данных отсутствует, сколько времени потребуется для их автоматической очистки? ФИФО. Настройка времени ожидания может повлиять на стабильность и эффективность передачи данных через последовательный порт.
/**
* uart_update_timeout - update per-port FIFO timeout.
* @port: uart_port structure describing the port
* @cflag: termios cflag value
* @baud: speed of the port
*
* Set the port FIFO timeout value. The @cflag value should
* reflect the actual hardware settings.
*/
void
uart_update_timeout(struct uart_port *port, unsigned int cflag,
unsigned int baud)
{
unsigned int bits;
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5:
bits = 7;
break;
case CS6:
bits = 8;
break;
case CS7:
bits = 9;
break;
default:
bits = 10;
break; /* CS8 */
}
if (cflag & CSTOPB)
bits++;
if (cflag & PARENB)
bits++;
/*
* The total number of bits to be transmitted in the fifo.
*/
bits = bits * port->fifosize;
/*
* Figure the timeout to send the above number of bits.
* Add .02 seconds of slop
*/
port->timeout = (HZ * bits) / baud + HZ/50;
}
bits
。в соответствии с cflag в CSIZE Флаговый бит, определяет количество бит в каждом байте (5, 6, 7 или 8 немного), и в соответствии с CSTOPB и PARENB Бит флага, увеличение количества стоповых битов и битов четности.bits
умножить на FIFO размера, чтобы получить общее количество битов, которые необходимо передать.uart_match_port
в соответствии с Атрибуты двух портов сравниваются, чтобы увидеть, являются ли два последовательных порта согласованными.
/*
* Are the two ports equivalent?
*/
int uart_match_port(struct uart_port *port1, struct uart_port *port2)
{
if (port1->iotype != port2->iotype)
return 0;
switch (port1->iotype) {
case UPIO_PORT:
return (port1->iobase == port2->iobase);
case UPIO_HUB6:
return (port1->iobase == port2->iobase) &&
(port1->hub6 == port2->hub6);
case UPIO_MEM:
case UPIO_MEM32:
case UPIO_MEM32BE:
case UPIO_AU:
case UPIO_TSI:
return (port1->mapbase == port2->mapbase);
}
return 0;
}
uart_console_write
используется для Воляконсольинформация Писать Введите последовательный порт。
существуетвстроенная система,Часто необходимо перенаправить вывод консоли на последовательный порт.,для отладкиирегистрация。Долженфункция Осуществленный Воляодиннить Писать Введите последовательный портизоперация, среди которому необходимо преобразовать строку с новой строки в возврат каретки и новую строку.
/**
* uart_console_write - write a console message to a serial port
* @port: the port to write the message
* @s: array of characters
* @count: number of characters in string to write
* @putchar: function to write character to port
*/
void uart_console_write(struct uart_port *port, const char *s,
unsigned int count,
void (*putchar)(struct uart_port *, int))
{
unsigned int i;
for (i = 0; i < count; i++, s++) {
if (*s == '\n')
putchar(port, '\r');
putchar(port, *s);
}
}
Реализация функции в основном проходит все символы в строке.,и записывает каждый символ в последовательный порт. существовать до написания символов,Необходимо определить, является ли символ да символом новой строки. Если далин сломается,Сначала вам необходимо преобразовать его в возврат каретки и перевод строки.,Снова Писать Введите последовательный порт。
Часть, которая соединяется с нижним слоем, ядром. основнойда Обеспечивает два интерфейса:
1. uart_register_driver (один вызов) 2. uart_add_one_port (вызывается несколько раз)
Через эти два интерфейса чип может подключить свой собственный UART к драйверу UART ядра Linux.
Части, которые производители чипов должны разработать и внедрить самостоятельно:
1. структура uart_drvier (одна) 2. Структура uart_port (несколько) 3. набор операций uart_ops для последовательного порта (может быть один, может быть несколько)
Итак, со структурной точки зрения весь процесс стыковки выглядит следующим образом:
Здесь есть одна вещь, которая требует особого внимания,существуют стыковочная часть нижнего слоя,Kernel определенныйодин Структура Вызов:struct uart_ops
существовать tty слой, правильно tty_driver Во время инициализации (serial_core.c) вызовите:
tty_set_operations(normal, &uart_ops);
И его реализация да:
void tty_set_operations(struct tty_driver *driver,const struct tty_operations *op)
{
driver->ops = op;
};
EXPORT_SYMBOL(tty_set_operations);
Ты видел это, да прошел? ****tty_operations *op****,так,существовать tty_driver
установлен uart_ops
Не то struct uart_ops
,И этот файл Serial_core.c определен в файле:
static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_show = uart_proc_show,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.set_serial = uart_set_info_user,
.get_serial = uart_get_info_user,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
То же имя,Но да это не та же самая структура,Легко быть ослепленным~~
https://blog.csdn.net/zhoutaopower/article/details/99289550
https://www.cnblogs.com/timemachine213/p/14317462.html
https://blog.csdn.net/u011728480/article/details/105676239