Примеры кода драйверов для различных платформ
В этом примере минидрайвер для платы Media5200b инициализирует последовательный порт, задает скорость передачи и сохраняет символы в области данных. По окончании загрузки системы программа может считывать область данных и извлекать из нее буферизованные символы. Особенности реализации драйвера:
Прототип функции-обработчика минидрайвера имеет следующий вид:
int mdriver_handler( int state, void *data );
С помощью параметра state обработчик определяет, на каком этапе процесса загрузки он вызывается. См. описание функции mdriver_add() в главе Быстрая активация устройств настоящего руководства. Параметр data представляет собой виртуальный указатель на область данных, которая выделена минидрайверу. В этом примере драйвера исходный код функции-обработчика выглядит следующим образом:
struct mserial_data{psc_regs *psc;psc_regs *psc_k;uint16_t intr_calls;uint16_t ncalls;uint16_t errors;uint16_t last_count;uint16_t data_len;};/** Функция инициализации оборудования, которая вызывается только при первом выполнении* минидрайвера. В этом примере значения Baud, Clk и port жестко закодированы* baud = 14400* clk = 132000000* port = PSC1*/static psc_regs * init_hw(){/* жесткое кодирование параметров PSC1, baud и clk */int baud = 14400;int clk = 132000000;paddr64_t psc_base = MGT5200_MBAR_BASE + 0x2000;psc_regs *psc;gpio_std_regs *gpio;if ( (psc = (psc_regs *)(startup_memory_map( 0xA0, psc_base,PROT_READ | PROT_WRITE | PROT_NOCACHE ))) == 0 )return (0);if ( (gpio = (gpio_std_regs *)(startup_memory_map( 0x40, (MGT5200_MBAR_BASE + 0x0b00),PROT_READ | PROT_WRITE | PROT_NOCACHE ))) == 0 ){startup_memory_unmap( (void *)psc );return (0);}baud = ((clk / baud) + 16) / 32; /* вычисление скорости приема */psc->CR = PSC_CR_TXD | PSC_CR_RXD;psc->SICR = 0x00000000; /* запись в SICR: SIM2 = режим uart,dcd не влияет на передачу */psc->SR_CSR = 0xdd00; /* запись в CSR: скорость приема/передачи от таймеров */psc->CTUR = (baud >> 8) & 0xff; /* запись в CTUR: старший байт делителя частоты */psc->CTLR = baud & 0xff; /* запись в CTLR: младший байт делителя частоты */psc->CR = 0x20; /* запись в CR: сброс RX и RXFIFO */psc->CR = 0x30; /* запись в CR: сброс TX и TXFIFO */psc->CR = 0x40; /* запись в CR: сброс состояния ошибки */psc->CR = 0x50; /* запись в CR: сброс прерывания по изменению состояния или состоянию break */psc->CR = 0x10; /* запись в CR: сброс указателя MR */psc->IPCR_ACR = 0x00; /* запись в ACR: запрет прерываний по изменению состояний CTS/DCD */psc->ModeReg = 0x73; /* запись в MR1: 8 бит данных, без контроля четности */psc->ModeReg = 0x07; /* запись в MR2: обычный режим, один стоп-бит */psc->OP1 = 1; /* запись в OP1: установка RTS для отправки */psc->ISR_IMR = 0x0000; /* запись в IMR: маскирование прерывания RxRDY и TxRDY */psc->RFALARM = 511;psc->TFALARM = 0xF8;psc->RFCNTL = 0x04;gpio->port_config = (gpio->port_config & GPIO_PSC1_MASK) | GPIO_PSC1_UART_ENABLE;/* включение передатчиков и приемников */psc->CR = 0x05; /* запись в CR: включение передачи и приема. */psc->ISR_IMR = PSC_ISR_RxRDY;startup_memory_unmap( (void *)gpio );/* возврат назначенного указателя на регистры */return (psc);}int mini_serial( int state, void *data ){uint8_t *bp = 0;paddr64_t psc_base = MGT5200_MBAR_BASE + 0x2000;uint16_t count;uint8_t *dptr;struct mserial_data *mdata;mdata = (struct mserial_data *)data;dptr = (uint8_t *)(mdata + 1);if ( state == MDRIVER_INTR_ATTACH ){/* запрещение прерываний от последовательного порта */mdata->psc->ISR_IMR = 0x0000;/* очистка аппаратного буфера */count = 0;while ( mdata->psc->SR_CSR & PSC_SR_RxRDY ){bp = (uint8_t *)&mdata->psc->RB_TB;dptr[mdata->data_len+count] = *bp;count++;}mdata->last_count = count;count = count + mdata->data_len;mdata->data_len = count;/* сброс ошибок */if ( (mdata->psc->SR_CSR & PSC_SR_ORERR) || (mdata->psc->SR_CSR & PSC_SR_RB) ||(mdata->psc->SR_CSR & PSC_SR_FE) || (mdata->psc->SR_CSR & PSC_SR_PE) ){mdata->psc->CR = PSC_CR_RESET_ERR;count = mdata->errors + 1;mdata->errors = count;}return (1);} else if ( state == MDRIVER_INIT ){/* при первом вызове - инициализация оборудования и настройка данных */mdata->psc = init_hw();if ( mdata->psc == 0 )return (1);mdata->intr_calls = 0;mdata->ncalls = 0;mdata->errors = 0;mdata->data_len = 0;} else if ( state == MDRIVER_STARTUP_PREPARE ){/* по окончании запуска применяется функция callout_io_map */if ( (mdata->psc_k = (psc_regs *)(callout_memory_map( 0xA0, psc_base, PROT_READ |PROT_WRITE | PROT_NOCACHE ))) == 0 ){/* серьезная ошибка, отключение драйвера */mdata->psc->ISR_IMR = 0x0000;return (1);}}/* подсчет количества вызовов драйвера */count = mdata->ncalls + 1;mdata->ncalls = count;if ( state == MDRIVER_PROCESS ){/* вызывается при прерывании */count = mdata->intr_calls + 1;mdata->intr_calls = count;}count = 0;while ( mdata->psc->SR_CSR & PSC_SR_RxRDY ){bp = (uint8_t *)&mdata->psc->RB_TB;dptr[mdata->data_len+count] = *bp;count++;}mdata->last_count = count;count = count + mdata->data_len;mdata->data_len = count;/* сброс ошибок */if ( (mdata->psc->SR_CSR & PSC_SR_ORERR) || (mdata->psc->SR_CSR & PSC_SR_RB) ||(mdata->psc->SR_CSR & PSC_SR_FE) || (mdata->psc->SR_CSR & PSC_SR_PE) ){mdata->psc->CR = PSC_CR_RESET_ERR;count = mdata->errors + 1;mdata->errors = count;}if ( state == MDRIVER_STARTUP_FINI )mdata->psc = mdata->psc_k;return (0);}
Поскольку в этом примере функция-обработчик хранит информацию о вызовах, для облегчения доступа к области данных создана специальная структура. Принятые символы помещаются в область данных.
Драйверу необходим доступ к регистрам последовательного порта. В ходе инициализации функция startup_memory_map() отображает регистры, а полученный указатель сохраняется в области данных на этапе запуска. Функция startup_memory_map() отображает регистры. По окончании этапа запуска обработчик должен получить доступ к регистрам с помощью функции callout_memory_map().
В состоянии MDRIVER_INIT
инициализируется область данных и настраивается последовательный порт; обработчик входит в это состояние только один раз. Здесь вызывается функция startup_memory_map(), которая отображает аппаратные регистры, а указатель сохраняется в области данных и используется для доступа к последовательному порту, когда обработчик вызывается с состоянием STARTUP_MDRIVER_PREPARE
. На этом этапе можно вызвать функцию callout_memory_map() и сохранить указатель в области данных, однако следует по-прежнему использовать указатель, который возвращен функцией startup_memory_map(). После вызова с состоянием MDRIVER_STARTUP_FINI
обработчик выполняет все операции над оборудованием с использованием указателя, который возвращен функцией callout_memory_map(). Этот указатель применяется во всех последующих вызовах обработчика минидрайвера.
В состоянии MDRIVER_INTR_ATTACH
обработчик запрещает прерывание устройства и возвращает значение 1
, запрашивая останов.
После написания функции-обработчика необходимо зарегистрировать ее в программе запуска и выделить требуемый объем оперативной памяти для области данных. Для этого используются следующие функции:
paddr_t alloc_ram( phys_addr, size, alignment );int mdriver_add( name, interrupt, handler, data_paddr, data_size );
Поскольку эти функции выделяют память и используют номер прерывания, перед их вызовом необходимо инициализировать оперативную память с помощью функции init_raminfo() и добавить информацию о прерываниях на системную страницу с помощью функции init_intrinfo(). Функция main() в файле main.c
программы запуска выглядит следующим образом:
...paddr_t mdrvr_addr;.../* Получение информации обо всей свободной оперативной памяти системы. */init_raminfo();/* В виртуальной системе нам необходимо инициализировать таблицы страниц */if( shdr->flags1 & STARTUP_HDR_FLAGS1_VIRTUAL )init_mmu();/* Следующие процедуры могут зависеть от оборудования или системы, и при необходимости в них вносятся изменения. */init_intrinfo();mdrvr_addr = alloc_ram( ~0L, 65536, 1 ); /* получение 64 Кбайт */mdriver_add( "mini-serial", 0, mini_serial, mdrvr_addr, 65536 );...
В этом примере мы выделили 64 Кбайт памяти и зарегистрировали функцию-обработчика минидрайвера с именем mini-serial.
Минидрайвер готов, и теперь необходимо пересобрать код запуска и загрузочный образ. Чтобы проверить корректность работы минидрайвера, можно добавить отладочную информацию в функцию-обработчика или написать приложение, которое считывает область данных минидрайвера и отображает ее содержимое.
Плата Media5200b включает в себя один последовательный порт, через который минидрайвер получает данные. Для тестирования этого драйвера следует создать загрузочный образ, который настраивает протокол TCP/IP и позволяет подключаться к плате с помощью telnet. Поскольку последовательный порт используется в качестве источника данных, не следует запускать его драйвер из образа, а также выполнять отладочную печать и выводить подробную информацию в модуле procnto. Запишите созданный образ во встроенную флеш-память, чтобы загрузить из него систему.
Соедините плату Media5200b с главным компьютером с помощью нуль-модемного кабеля. Минидрайвер работает со скоростью 14400 бит/с без управления потоком передачи данных. Необходимо таким же образом настроить последовательный порт главного компьютера. Начните передавать символы в последовательный порт на главном компьютере:
...uint8_t test_data[20] = {"012345678987654321"};...if ( (fd = open( "/dev/ser1", O_RDWR )) == -1 ){fprintf( stderr, "Unable to open device: %s (errno=%d\n", device, errno );return (-1);}for ( ;; ){write( fd, test_data + index, 1 );index++;if ( index == 19 )index = 0;}
Этот код отправляет набор символов в последовательный порт, чтобы минидрайвер принял их.
Теперь загрузите плату Media5200b и подключитесь к ней по протоколу telnet.
После установления соединения запустите пример полнофункционального драйвера mdrvr-serpsc, который выполняет следующие действия:
Поскольку mdrvr-serpsc является примером драйвера последовательного порта, нет необходимости запускать драйвер devc-serpsc.
Эти временные характеристики указаны для загрузочного образа со следующими свойствами:
mdriver_max | Количество вызовов | Средний интервал между вызовами |
---|---|---|
1 Кбайт | 855 | 514 мкс |
4 Кбайт | 231 | 2.04 мс |
16 Кбайт | 75 | 8.13 мс |
Время загрузки | Время первого вызова |
---|---|
453.2 мс | 1 мс |
![]() | Время загрузки — интервал между запуском системы и началом работы первого приложения. Время первого вызова — интервал между запуском начального загрузчика (IPL) и первым вызовом минидрайвера (MDRIVER_INIT ). |
В этом примере минидрайвер для платы Media5200b инициализирует последовательный порт с определенной скоростью передачи и сохраняет символы в области данных. По окончании загрузки системы программа может считывать область данных и извлекать из нее буферизованные символы.
Особенности реализации драйвера:
Прототип функции-обработчика минидрайвера имеет следующий вид:
int mdriver_handler( int state, void *data );
С помощью параметра state обработчик определяет, на каком этапе процесса загрузки он вызывается. См. описание функции mdriver_add() в главе Быстрая активация устройств настоящего руководства. Параметр data представляет собой виртуальный указатель на область данных, которая выделена минидрайверу. В этом примере драйвера исходный код функции-обработчика выглядит следующим образом:
struct mserial_data{uintptr_t port;uintptr_t port_k;uint16_t intr_calls;uint16_t ncalls;uint16_t errors;uint16_t err;uint16_t last_count;uint16_t data_len;};void Delay( int n ){for ( unsigned int i = 0; i < n; i++ );}uintptr_t init_hw(){int cnt;uint64_t base = SH_SCIF_BASE;int baud = 14400;int clk = 33333333;unsigned port;if ( (port = startup_io_map( 120, base )) == NULL )return (NULL);cnt = clk / (baud * 64 / 2) - 1; /* предполагается, что SCSMR1.CKS = 0 *//* выбор часов */out16( port + SH_SCIF_SCSCR_OFF, 0x0 );Delay( 1 );/* включение состояния сброса регистра FIFO */out16( port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST );/** формат передачи данных* 8 бит, без контроля четности* CKS = 0*/out16( port + SH_SCIF_SCSMR_OFF, 0 );Delay( 1 );/* скорость передачи */out8( port + SH_SCIF_SCBRR_OFF, cnt );Delay( 1 );/* отключение состояния сброса FIFO */set_port16( port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST, 0 );/* управление FIFO */out16( port + SH_SCIF_SCFCR_OFF + 4, 0x0 );Delay( 1 );out8( port + SH_SCIF_SCFTDR_OFF, 0 );Delay( 1 );out16( port + SH_SCIF_SCSPTR_OFF + 4, 0x41 );out16( port + SH_SCIF_SCFCR_OFF, 0x30 | 0x200 | SH_SCIF_SCFCR_MCE );out16( port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_TE |SH_SCIF_SCSCR_TIE | SH_SCIF_SCSCR_RIE );Delay( 1 );out16( port + SH7760_SCIF_SCSPTR_OFF, SH_SCIF_SCSPTR_RTSIO );out16( port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE );return (port);}int mini_serial( int state, void *data ){uint8_t *bp = 0;uint16_t count;uint8_t *dptr, c;struct mserial_data *mdata;int num, i;int pending_interrupts;mdata = (struct mserial_data *)data;dptr = (uint8_t *)(mdata + 1);if ( state == MDRIVER_INTR_ATTACH ){/* запрещение прерываний от последовательного порта */set_port16( mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0 );return (1);} else if ( state == MDRIVER_INIT ){/* При первом вызове - инициализация оборудования и настройка данных */mdata->port = init_hw();if ( mdata->port == 0 )return (1);mdata->intr_calls = 0;mdata->ncalls = 0;mdata->errors = 0;mdata->data_len = 0;} else if ( state == MDRIVER_STARTUP_PREPARE ){/* по окончании запуска применяется функция callout_io_map */if ( (mdata->port_k = callout_io_map( 0x120, SH_SCIF_BASE )) == 0 ){/* серьезная ошибка, отключение драйвера */set_port16( mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0 );return ( 1 );}}/* подсчет количества вызовов драйвера */count = mdata->ncalls + 1;mdata->ncalls = count;if ( state == MDRIVER_PROCESS ){/* вызывается при прерывании */count = mdata->intr_calls + 1;mdata->intr_calls = count;}/* считывание данных из последовательного порта и сброс всех ошибок *//* обработка сообщений об ошибках: переполнение, синхронизация, четность */pending_interrupts = in16( mdata->port + SH_SCIF_SCFSR_OFF );if ( in8( mdata->port + SH7760_SCIF_SCLSR_OFF ) & SH_SCIF_SCLSR_ORER ){set_port16( mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0 );}if ( pending_interrupts & SH_SCIF_SCFSR_PER ){set_port16( mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_PER | SH_SCIF_SCFSR_ER |SH_SCIF_SCFSR_DR, 0 );}if ( pending_interrupts & SH_SCIF_SCFSR_FER ){set_port16( mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_FER | SH_SCIF_SCFSR_ER |SH_SCIF_SCFSR_DR, 0 );}if ( pending_interrupts & SH_SCIF_SCFSR_BRK ){set_port16( mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_BRK | SH_SCIF_SCFSR_DR, 0 );}/* считывание данных */if ( in16( mdata->port + SH7760_SCIF_SCRFDR_OFF ) ){c = in8( mdata->port + SH_SCIF_SCFRDR_OFF );set_port16( mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_RDF, 0 );dptr[mdata->data_len] = c;mdata->data_len = mdata->data_len + 1;}if ( pending_interrupts & (SH_SCIF_SCFSR_TDFE|SH_SCIF_SCFSR_TEND) ){set_port16( mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_TIE, 0 );}/* сброс всех прерываний */set_port16( mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0 );set_port16( mdata->port + SH_SCIF_SCFSR_OFF, 0xff, 0 );if ( state == MDRIVER_STARTUP_FINI )mdata->port = mdata->port_k;return (0);}
Поскольку в этом примере функция-обработчик хранит информацию о вызовах, для облегчения доступа к области данных создана специальная структура. Принятые символы помещаются в область данных.
В состоянии MDRIVER_INIT
инициализируется область данных и настраивается последовательный порт; обработчик входит в это состояние только один раз. Здесь вызывается функция startup_memory_map(), которая отображает аппаратные регистры, а указатель сохраняется в области данных и используется для доступа к последовательному порту, когда обработчик вызывается с состоянием STARTUP_MDRIVER_PREPARE
.
На этом этапе также вызывается функция callout_io_map() и указатель сохраняется в области данных, однако следует по-прежнему использовать указатель, который возвращен функцией startup_io_map(). После вызова с состоянием MDRIVER_STARTUP_FINI
обработчик выполняет все операции над оборудованием с использованием указателя, который возвращен функцией callout_io_map(). Этот указатель применяется во всех последующих вызовах обработчика минидрайвера.
В состоянии MDRIVER_INTR_ATTACH
обработчик запрещает прерывание устройства и возвращает значение 1
, запрашивая останов.
После написания функции-обработчика необходимо зарегистрировать ее в программе запуска и выделить требуемый объем оперативной памяти для области данных. Для этого используются следующие функции:
paddr_t alloc_ram( phys_addr, size, alignment );int mdriver_add( name, interrupt, handler, data_paddr, data_size );
Поскольку эти функции выделяют память и используют номер прерывания, перед их вызовом необходимо инициализировать оперативную память с помощью функции init_raminfo() и добавлять информацию о прерываниях на системную страницу с помощью функции init_intrinfo(). Функция main() в файле main.c
программы запуска выглядит следующим образом:
...paddr_t mdrvr_addr;.../* Получение информации обо всей свободной оперативной памяти системы. */init_raminfo();/* В виртуальной системе нам необходимо инициализировать таблицы страниц */if ( shdr->flags1 & STARTUP_HDR_FLAGS1_VIRTUAL )init_mmu();/* Следующие процедуры могут зависеть от оборудования или системы, и при необходимости в них вносятся изменения */init_intrinfo();mdrvr_addr = alloc_ram( ~0L, 65536, 1 ); /* grab 64k */mdriver_add( "mini-serial", 0, mini_serial, mdrvr_addr, 65536 );...
В этом примере мы выделили 64 Кбайт памяти и зарегистрировали функцию-обработчика минидрайвера с именем mini-serial.
Минидрайвер готов, и теперь необходимо пересобрать программу запуска и загрузочный образ. Чтобы проверить корректность работы минидрайвера, можно добавить отладочную информацию в функцию-обработчика или написать приложение, которое считывает область данных минидрайвера и отображает ее содержимое.
Плата Biscayne включает в себя отладочный последовательный порт, через который минидрайвер получает данные. Для тестирования этого драйвера следует создать загрузочный образ, который настраивает протокол TCP/IP и позволяет подключаться к плате с помощью telnet. Поскольку последовательный порт используется в качестве источника данных, не следует запускать его драйвер из образа, а также выполнять отладочную печать и выводить подробную информацию в модуле procnto. Запишите созданный образ во встроенную флеш-память, чтобы загрузить из него систему.
Соедините плату Biscayne с главным компьютером с помощью нуль-модемного кабеля. Поскольку минидрайвер работает на скорости передачи 14400 бит/с, необходимо таким же образом настроить последовательный порт главного компьютера. Начните передавать символы в последовательный порт на главном компьютере:
...uint8_t test_data[20] = {"012345678987654321"};...if ( (fd = open( "/dev/ser1", O_RDWR )) == -1 ){fprintf( stderr, "Unable to open device: %s (errno=%d\n", device, errno );return (-1);}for ( ;; ){write( fd, test_data + index, 1 );index++;if ( index == 19 )index = 0;}
Этот код отправляет набор символов в последовательный порт, чтобы минидрайвер принял их.
Теперь загрузите плату Biscayne и подключитесь к ней по протоколу telnet. После установления соединения запустите пример полнофункционального драйвера mdrvr-sescif, который выполняет следующие действия:
Поскольку mdrvr-serscif является примером драйвера последовательного порта, нет необходимости запускать драйвер devc-sersci.
Эти временные характеристики указаны для загрузочного образа со следующими свойствами:
mdriver_max | Количество вызовов | Средний интервал между вызовами |
---|---|---|
1 Кбайт | 715 | 59 мкс |
4 Кбайт | 139 | 194 мкс |
16 Кбайт | 65 | 934 мкс |
Время загрузки | время первого вызова |
---|---|
636 мс | 1 мс |
![]() | Время загрузки — интервал между запуском системы и началом работы первого приложения. Время первого вызова — интервал между запуском начального загрузчика (IPL) и первым вызовом минидрайвера (MDRIVER_INIT ). |
В этом примере минидрайвер для платы OMAP 5912 инициализирует последовательный порт с определенной скоростью передачи и сохраняет символы в области данных. По окончании загрузки системы программа может считывать область данных и извлекать из нее буферизованные символы. Особенности реализации драйвера:
Прототип функции-обработчика минидрайвера имеет следующий вид:
int mdriver_handler( int state, void *data );
С помощью параметра state обработчик определяет, на каком этапе процесса загрузки он вызывается. См. описание функции mdriver_add() в главе Быстрая активация устройств настоящего руководства. Параметр data представляет собой виртуальный указатель на область данных, которая выделена минидрайверу. В этом примере драйвера исходный код функции-обработчика выглядит следующим образом:
struct mserial_data{uintptr_t port;uintptr_t port_k;uint16_t intr_calls;uint16_t ncalls;uint16_t errors;uint16_t err;uint16_t last_count;uint16_t data_len;};#ifndef write_omap#define write_omap( __port,__val ) out8( __port, __val )#endif#ifndef read_omap#define read_omap( __port ) in8( __port )#endif/** инициализация интерфейса UART* базовый адрес = 0xfffb0000* скорость передачи = 14400* данные = 8 n 1*/uintptr_t init_hw(){unsigned value = 0;unsigned msr, c;uintptr_t port;uint64_t base = 0xfffb0000;int baud = 14400;if ( (port = startup_io_map( OMAP_UART_SIZE, base )) == 0 )return (0);/* установка в LCR специального байта, который разрешает доступ к регистру* расширенных функций (Enhanced Feature Register, EFR) */write_omap( port + OMAP_UART_LCR, 0xbf );/* отключение программного управления потоком передачи, включение записи в* MCR[7:5], FCR[5:4] и IER[7:4] */write_omap( port + OMAP_UART_EFR, 0x10 );write_omap( port + OMAP_UART_LCR, 0 );/* установка бита 6 регистра MCR для разрешения доступа к регистрам TCR и TLR */write_omap( port + OMAP_UART_MCR, 0x40 );value = 0x0;/* настройка приемного FIFO-буфера в TCR - прием начинается с позиции 0 и завершается* в позиции, соответствующей размеру буфера */write_omap( port + OMAP_UART_TCR,value >> 4 );write_omap( port + OMAP_UART_TLR, value );/* запрет доступа к TCR и TLR */write_omap( port + OMAP_UART_MCR, 0x00 );write_omap( port + OMAP_UART_SCR, 0x00 );write_omap( port + OMAP_UART_LCR, 0xbf );/* запрет доступа к MCR[7:5], FCR[5:4] и IER[7:4],* отключение автоматического и программного управления потоком передачи */write_omap( port + OMAP_UART_EFR, 0x00 );/* включение защелки делителя - при записи в регистр LCR значения,* отличного от 0xbf, он возвращается в "обычный" режим */write_omap( port + OMAP_UART_LCR, 0x80 );/* baud and clk */value = 48000000 / (16 * baud);write_omap( port + OMAP_UART_DLL, value );write_omap( port + OMAP_UART_DLH, (value >> 8) & 0xff );write_omap( port + OMAP_UART_LCR, 0x13 );/* включение DTR, RTS */c = read_omap( port + OMAP_UART_MCR );write_omap( port + OMAP_UART_MCR, (c & ~OMAP_MCR_DTR | OMAP_MCR_RTS) |OMAP_MCR_DTR | OMAP_MCR_RTS );/* в соответствии с Национальным перечнем ошибок необходимо ждать* опустошения регистра хранения передаваемых данных */do {} while ((read_omap( port + OMAP_UART_LSR ) & OMAP_LSR_TXRDY) == 0);/* сброс устройства, чтобы получить перепад уровня на линии прерывания шины* включение out2 (передача прерывания на шину) */c = read_omap( port + OMAP_UART_MCR );write_omap( port + OMAP_UART_MCR, (c & ~OMAP_MCR_OUT2) | OMAP_MCR_OUT2 );write_omap( port + OMAP_UART_IER, 0 ); /* запрещение всех прерываний */read_omap( port + OMAP_UART_LSR ); /* сброс прерывания состояния линии */read_omap( port + OMAP_UART_RHR ); /* сброс прерывания приема */read_omap( port + OMAP_UART_THR ); /* сброс прерывания передачи */read_omap( port + OMAP_UART_MSR ); /* сброс прерывания модема *//* разрешение источников прерываний */write_omap( port + OMAP_UART_IER, 0x01 );/* считывание текущего состояния MSR */msr = read_omap( port + OMAP_UART_MSR );return (port);}int mini_serial( int state, void *data ){uint16_t count;uint8_t *dptr, c;struct mserial_data *mdata;unsigned lsr, iir;mdata = (struct mserial_data *)data;dptr = (uint8_t *)(mdata + 1);if ( state == MDRIVER_INTR_ATTACH ){/* запрещение прерываний от последовательного порта */write_omap( mdata->port + OMAP_UART_IER, 0x00 );return (1);} else if ( state == MDRIVER_INIT ){if ( (mdata->port = init_hw()) == 0 )return (1);/* сброс счетчиков области данных */mdata->intr_calls = 0;mdata->ncalls = 0;mdata->errors = 0;mdata->data_len = 0;} else if ( state == MDRIVER_STARTUP_PREPARE ){/* по окончании запуска применяется функция callout_io_map */if ( (mdata->port_k = callout_io_map( OMAP_UART_SIZE, 0xfffb0000 )) == 0 ){/* серьезная ошибка, отключение драйвера */write_omap( mdata->port + OMAP_UART_IER, 0x00 );return (1);}}/* подсчет количества вызовов драйвера */count = mdata->ncalls + 1;mdata->ncalls = count;if ( state == MDRIVER_PROCESS ){/* вызывается при прерывании */count = mdata->intr_calls + 1;mdata->intr_calls = count;iir = read_omap( mdata->port + OMAP_UART_IIR ) & 0x07;if ( iir == OMAP_II_RX ) /* прерывание приема */{do {dptr[mdata->data_len] = read_omap( mdata->port + OMAP_UART_RHR ) & 0xff;mdata->data_len = mdata->data_len + 1;lsr = read_omap( mdata->port + OMAP_UART_LSR );/* проверка ошибок */if ( (lsr & (OMAP_LSR_RCV_FIFO | OMAP_LSR_BI | OMAP_LSR_OE |OMAP_LSR_FE | OMAP_LSR_PE)) != 0 ){/* считывание любых входных данных, которые находятся в буфере, чтобы "проглотить"* побочные данные, связанные с состоянием break, ошибкой четности и др. */c = read_omap( mdata->port + OMAP_UART_RHR );c = c;}} while (lsr & OMAP_LSR_RXRDY);}} else {/* опрос данных */while ( read_omap( mdata->port + OMAP_UART_LSR ) & OMAP_LSR_RXRDY ){dptr[mdata->data_len] = read_omap( mdata->port + OMAP_UART_RHR ) & 0xff;mdata->data_len = mdata->data_len + 1;}}if ( state == MDRIVER_STARTUP_FINI )mdata->port = mdata->port_k;return (0);}
Поскольку в этом примере функция-обработчик хранит информацию о вызовах, для облегчения доступа к области данных создана специальная структура. Принятые символы помещаются в область данных.
В состоянии MDRIVER_INIT
инициализируется область данных и настраивается последовательный порт; обработчик входит в это состояние только один раз. Здесь вызывается функция startup_io_map(), которая отображает аппаратные регистры, а указатель сохраняется в области данных и используется для доступа к последовательному порту, когда обработчик вызывается с состоянием STARTUP_MDRIVER_PREPARE
.
Здесь вызывается функция callout_io_map() и указатель сохраняется в области данных, однако следует по-прежнему использовать указатель, который возвращен функцией startup_io_map(). После вызова с состоянием MDRIVER_STARTUP_FINI
обработчик выполняет все операции над оборудованием с использованием указателя, который возвращен функцией callout_io_map().
Этот указатель применяется во всех последующих вызовах обработчика минидрайвера. В состоянии MDRIVER_INTR_ATTACH
обработчик запрещает прерывание устройства и возвращает значение 1
, запрашивая останов.
После написания функции-обработчика необходимо зарегистрировать ее в программе запуска и выделить требуемый объем оперативной памяти для области данных. Для этого используются следующие функции:
paddr_t alloc_ram( phys_addr, size, alignment );int mdriver_add( name, interrupt, handler, data_paddr, data_size );
Поскольку эти функции выделяют память и используют номер прерывания, перед их вызовом необходимо инициализировать оперативную память с помощью функции init_raminfo() и добавлять информацию о прерываниях на системную страницу с помощью функции init_intrinfo(). Функция main() в файле main.c
программы запуска выглядит следующим образом:
...paddr_t mdrvr_addr;...init_intrinfo();init_qtime();/* выделение минидрайверу 64 Кбайт оперативной памяти */mdrvr_addr = alloc_ram( ~0L, 64*1024, 1 );/* добавление примера минидрайвера, который только собирает данные */mdriver_add( "mini-data", 0, mini_data, mdrvr_addr, 64 * 1024 );/* добавление примера минидрайвера последовательного порта *//* mdriver_add( "mini-serial", 46, mini_serial, mdrvr_addr, 64 * 1024 ); */...
В этом примере мы выделили 64 Кбайт памяти и зарегистрировали функцию-обработчика минидрайвера с именем mini-serial.
Минидрайвер готов, и теперь необходимо пересобрать программу запуска и загрузочный образ. Чтобы проверить корректность работы минидрайвера, можно добавить отладочную информацию в функцию-обработчика или написать приложение, которое считывает область данных минидрайвера и отображает ее содержимое.
Плата OMAP 5912 включает в себя отладочный последовательный порт, через который минидрайвер получает данные. Для тестирования этого драйвера следует создать загрузочный образ, который настраивает протокол TCP/IP и позволяет подключаться к плате с помощью telnet. Поскольку последовательный порт используется в качестве источника данных, не следует запускать его драйвер из образа, а также выполнять отладочную печать и выводить подробную информацию в модуле procnto. Запишите созданный образ во встроенную флеш-память, чтобы загрузить из него систему.
Соедините плату OMAP с главным компьютером с помощью нуль-модемного кабеля. Поскольку минидрайвер работает на скорости передачи 14400 бит/с, необходимо настроить последовательный порт главного компьютера аналогичным образом. Начните передавать символы в последовательный порт на главном компьютере:
...uint8_t test_data[20] = {"012345678987654321"};...if ( (fd = open( "/dev/ser1", O_RDWR )) == -1 ){fprintf( stderr, "Unable to open device: %s (errno=%d\n", device, errno );return (-1);}for ( ;; ){write( fd, test_data + index, 1 );index++;if ( index == 19 )index = 0;}
Этот код отправляет набор символов в последовательный порт, чтобы минидрайвер принял их.
Теперь загрузите плату OMAP 5912 и подключитесь к ней по протоколу telnet. После установления соединения запустите пример полнофункционального драйвера mdrvr-seromap, который выполняет следующие действия:
Поскольку mdrvr-seromap является примером драйвера последовательного порта, нет необходимости запускать драйвер devc-seromap.
Эти временные характеристики указаны для загрузочного образа со следующими свойствами:
mdriver_max | Количество вызовов | Средний интервал между вызовами |
---|---|---|
1 Кбайт | 782 | 93 мкс |
4 Кбайт | 229 | 352 мкс |
16 Кбайт | 91 | 1.38 мс |
Время загрузки | Время первого вызова |
---|---|
170 мс | 1 мс |
![]() | Время загрузки — интервал между запуском системы и началом работы первого приложения. Время первого вызова — интервал между запуском начального загрузчика (IPL) и первым вызовом минидрайвера (MDRIVER_INIT ). |
Предыдущий раздел: перейти