Saya mengembangkan modul kernel sederhana ini, yang mengemulasi port serial dengan menggunakan antrian FIFO dan pengatur waktu (baca dari perangkat keras : keluar dari antrian, tulis ke perangkat keras : masukkan ke dalam antrian).
Kode sumber ditampilkan berikutnya.
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/module.h>
#define TINY_SERIAL_DEBUG
#define pr_fmt(fmt) "tiny_serial: " fmt
#if defined(TINY_SERIAL_DEBUG)
#define DBG(fmt, ...) printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#else
#define DBG(fmt, ...) no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
#define DRIVER_AUTHOR "Me Me <[email protected]>"
#define DRIVER_DESC "Tiny serial driver"
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
#define DELAY_TIME 100// HZ * 2 /* 2 seconds per character */
#define TINY_DATA_CHARACTER 't'
#define TINY_SERIAL_MAJOR 240 //240 /* experimental range */
#define TINY_SERIAL_MINORS 1 //1 /* only have one minor */
#define UART_NR 1 /* only use one port */
#define TINY_SERIAL_NAME "ttytiny"
#define MY_NAME TINY_SERIAL_NAME
#define BUF_SIZE 4096
static char buf[BUF_SIZE];
static char *read_ptr;
static char *write_ptr;
static struct timer_list *timer;
static void serial_out(char data)
{
*write_ptr = data;
write_ptr++;
if (write_ptr >= buf + BUF_SIZE)
write_ptr = buf;
}
static void serial_in(char *data)
{
if (read_ptr == NULL) {
DBG("pointer is null !\n");
}
if (read_ptr && (read_ptr != write_ptr)) {
*data = *read_ptr;
read_ptr++;
if(read_ptr >= buf + BUF_SIZE)
read_ptr = buf;
}
}
static void tiny_stop_tx(struct uart_port *port)
{
DBG("tiny_stop_tx()\n");
}
static void tiny_stop_rx(struct uart_port *port)
{
DBG("tiny_stop_rx()\n");
}
static void tiny_enable_ms(struct uart_port *port)
{
DBG("tiny_enable_ms()\n");
}
static void tiny_rx_chars(struct uart_port *port, int size)
{
int i = 0;
char byte;
char flag;
struct tty_port *tty = &port->state->port;
if (size <= 0) {
return;
}
while (size--) {
serial_in(&byte);
DBG("read: 0x%2x\n", byte);
flag = TTY_NORMAL;
port->icount.rx++;
if (uart_handle_sysrq_char(port, byte)) {
DBG("found ignore char !\n");
goto ignore_char;
}
uart_insert_char(port, 0, 0, byte, flag);
ignore_char:
i ++;
}
tty_flip_buffer_push(tty);
DBG("push to user space !\n");
}
static int tiny_tx_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
int count;
DBG("tiny_tx_chars()\n");
if (port->x_char) {
DBG("wrote 0x%2x\r\n", port->x_char);
port->icount.tx++;
port->x_char = 0;
return 0;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
tiny_stop_tx(port);
return 0;
}
count = port->fifosize >> 1;
do {
DBG("wrote 0x%2x\r\n", xmit->buf[xmit->tail]);
serial_out(xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_empty(xmit))
break;
} while (--count > 0);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
tiny_stop_tx(port);
return ((port->fifosize >> 1) - count + 1);
}
static void tiny_start_tx(struct uart_port *port)
{
DBG("tiny_start_tx()\n");
}
static void tiny_timer(unsigned long data)
{
struct uart_port *port;
struct tty_port *tport;
int ret = 0;
DBG("tiny_timer()\n");
port = (struct uart_port *)data;
if (!port)
return;
if (!port->state)
return;
tport = &port->state->port;
/* resubmit the timer again */
timer->expires = jiffies + DELAY_TIME;
add_timer(timer);
/* see if we have any data to transmit */
ret = tiny_tx_chars(port);
tiny_rx_chars(port, ret);
}
static unsigned int tiny_tx_empty(struct uart_port *port)
{
DBG("tiny_tx_empty()\n");
return 0;
}
static unsigned int tiny_get_mctrl(struct uart_port *port)
{
DBG("tiny_get_mctrl()\n");
return 0;
}
static void tiny_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
DBG("tiny_set_mctrl()\n");
}
static void tiny_break_ctl(struct uart_port *port, int break_state)
{
DBG("tiny_set_mctrl()\n");
}
static void tiny_set_termios(struct uart_port *port,
struct ktermios *new, struct ktermios *old)
{
int baud, quot, cflag = new->c_cflag;
DBG("tiny_set_termios()\n");
/* get the byte size */
switch (cflag & CSIZE) {
case CS5:
DBG(" - data bits = 5\n");
break;
case CS6:
DBG(" - data bits = 6\n");
break;
case CS7:
DBG(" - data bits = 7\n");
break;
default: // CS8
DBG(" - data bits = 8\n");
break;
}
/* determine the parity */
if (cflag & PARENB)
if (cflag & PARODD)
DBG(" - parity = odd\n");
else
DBG(" - parity = even\n");
else
DBG(" - parity = none\n");
/* figure out the stop bits requested */
if (cflag & CSTOPB)
DBG(" - stop bits = 2\n");
else
DBG(" - stop bits = 1\n");
/* figure out the flow control settings */
if (cflag & CRTSCTS)
DBG(" - RTS/CTS is enabled\n");
else
DBG(" - RTS/CTS is disabled\n");
/* Set baud rate */
baud = uart_get_baud_rate(port, new, old, 9600, 115200);
quot = uart_get_divisor(port, baud);
}
static int tiny_startup(struct uart_port *port)
{
/* this is the first time this port is opened */
/* do any hardware initialization needed here */
DBG("tiny_startup()\n");
/* create our timer and submit it */
if (!timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!timer)
return -ENOMEM;
init_timer(timer);
}
timer->data = (unsigned long)port;
timer->expires = jiffies + DELAY_TIME;
timer->function = tiny_timer;
add_timer(timer);
return 0;
}
static void tiny_shutdown(struct uart_port *port)
{
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */
DBG("tiny_shutdown()\n");
/* shut down our timer */
del_timer(timer);
}
static const char *tiny_type(struct uart_port *port)
{
DBG("tiny_type()\n");
return "tinytty";
}
static void tiny_release_port(struct uart_port *port)
{
DBG("tiny_release_port()\n");
}
static int tiny_request_port(struct uart_port *port)
{
DBG("tiny_request_port()\n");
return 0;
}
static void tiny_config_port(struct uart_port *port, int flags)
{
DBG("tiny_config_port()\n");
}
static int tiny_verify_port(struct uart_port *port, struct serial_struct *ser)
{
DBG("tiny_verify_port()\n");
return 0;
}
static struct uart_ops tiny_ops = {
.tx_empty = tiny_tx_empty,
.set_mctrl = tiny_set_mctrl,
.get_mctrl = tiny_get_mctrl,
.stop_tx = tiny_stop_tx,
.start_tx = tiny_start_tx,
.stop_rx = tiny_stop_rx,
.enable_ms = tiny_enable_ms,
.break_ctl = tiny_break_ctl,
.startup = tiny_startup,
.shutdown = tiny_shutdown,
.set_termios = tiny_set_termios,
.type = tiny_type,
.release_port = tiny_release_port,
.request_port = tiny_request_port,
.config_port = tiny_config_port,
.verify_port = tiny_verify_port,
};
static struct uart_port tiny_port = {
.ops = &tiny_ops,
.line = 0,
.type = 104,
.iotype = SERIAL_IO_PORT,
.fifosize = 128,
.flags = ASYNC_BOOT_AUTOCONF,
.irq = 0,
};
static struct uart_driver tiny_reg = {
.owner = THIS_MODULE,
.driver_name = TINY_SERIAL_NAME,
.dev_name = TINY_SERIAL_NAME,
.major = TINY_SERIAL_MAJOR,
.minor = TINY_SERIAL_MINORS,
.nr = UART_NR,
};
static int __init tiny_init(void)
{
int result;
DBG(KERN_INFO "Tiny serial driver loaded\n");
result = uart_register_driver(&tiny_reg);
if (result) {
DBG("tiny_init() error!\n");
return result;
}
result = uart_add_one_port(&tiny_reg, &tiny_port);
if (result) {
DBG("uart_add_one_port() error!\n");
uart_unregister_driver(&tiny_reg);
}
read_ptr = buf;
write_ptr = buf;
return result;
}
module_init(tiny_init);
Lalu, saya menulis aplikasi pengujian sederhana yang mengonfigurasi pengaturan port (baud rate, parity, stop bits, dll) dan memulai transaksi tulis + baca, membaca string yang ditulis sebelumnya.
Kode sumber ditampilkan berikutnya.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#define PAR_NONE 0
#define PAR_EVEN 1
#define PAR_ODD 2
#define DATA_8BIT 1
#define DATA_7BIT 2
#define STOP_1BIT 1
#define STOP_2BIT 2
struct tiny_op {
int max_fd;
int fd;
fd_set rfds;
fd_set wfds;
fd_set efds;
struct timeval r_timeout;
struct timeval w_timeout;
};
static struct tiny_op tops;
static void set_termios(struct termios *termios, int baudrate, int parity, int bits, int stop)
{
termios->c_cflag |= CLOCAL | CREAD;
termios->c_cflag &= ~CSIZE;
switch (bits) {
case DATA_7BIT:
termios->c_cflag |= CS7;
break;
case DATA_8BIT:
termios->c_cflag |= CS8;
break;
default:
termios->c_cflag |= CS8;
break;
}
switch (parity) {
case PAR_NONE:
termios->c_cflag &= ~PARENB;
termios->c_cflag &= ~PARODD;
break;
case PAR_EVEN:
termios->c_cflag |= PARENB;
termios->c_cflag &= ~PARODD;
break;
case PAR_ODD:
termios->c_cflag |= PARENB;
termios->c_cflag |= PARODD;
break;
default:
termios->c_cflag &= ~PARENB;
termios->c_cflag &= ~PARODD;
break;
}
switch (stop) {
case STOP_1BIT:
termios->c_cflag &= ~CSTOPB;
break;
case STOP_2BIT:
termios->c_cflag |= CSTOPB;
break;
default:
termios->c_cflag &= ~CSTOPB;
break;
}
termios->c_iflag |= INPCK | ISTRIP;
termios->c_lflag = 0;
termios->c_oflag = 0;
termios->c_cc[VTIME] = 5;
termios->c_cc[VMIN] = 0;
cfsetspeed(termios, baudrate);
}
static int tiny_write(struct tiny_op *op, char *buff, int size)
{
int ret = -1;
int len = 0;
op->w_timeout.tv_sec = 5;
op->w_timeout.tv_usec = 0;
ret = select(op->max_fd, NULL, &op->wfds, &op->efds, &op->w_timeout);
if (ret < 0) {
printf("[TINY SERIAL] Select fallita (write) !\n");
return -1;
} if (0 == ret) {
printf("[TINY SERIAL] Select time-out (write) !\n");
} else {
if (FD_ISSET(op->fd, &op->efds)) {
printf("[TINY SERIAL] Attenzione: errore in scrittura!\n");
return -1;
}
if (FD_ISSET(op->fd, &op->wfds)) {
printf("[TINY SERIAL] Scrittura pronta, procedo !\n");
len = write(op->fd, (const void *)buff, size);
if (len == size) {
printf("[TINY SERIAL] Scrittura completata!\n");
} else {
printf("[TINY SERIAL] Scrittura parzialmente completata, scritti : = %d\n", len);
}
}
}
return 0;
}
static int tiny_read(struct tiny_op *op, char *buff, int size)
{
int ret = -1;
int len = 0;
op->r_timeout.tv_sec = 5;
op->r_timeout.tv_usec = 0;
ret = select(op->max_fd, &op->rfds, NULL, &op->efds, &op->r_timeout);
if (ret < 0) {
printf("[TINY SERIAL] Select fallita (read) !\n");
return -1;
} if (0 == ret) {
printf("[TINY SERIAL] Select time-out (read)!\n");
} else {
if (FD_ISSET(op->fd, &op->efds)) {
printf("[TINY SERIAL] Attenzione: errore in lettura!\n");
return -1;
}
if (FD_ISSET(op->fd, &op->rfds)) {
printf("[TINY SERIAL] Dati da leggere disponibili!\n");
len = read(op->fd, (void *)buff, size);
printf("[TINY SERIAL] Lettura da seriale: %s, len: %d\n", buff, len);
}
}
return 0;
}
int main(int argc, char **argv)
{
int fd = -1, ret = -1;
struct termios oldtio, newtio;
char *str = {"Hello world!!\0"};
char buff[strlen(str)];
if(argc == 1){
printf("[TINY DRIVER] Inserisci il device seriale da testare.\n");
return -1;
}
char * device = argv[1];
struct stat file_infos;
if(strlen(device) == 0){
printf("[TINY DRIVER] Seleziona un device corretto.\n");
return -1;
}
printf("[TINY DRIVER] Hai selezionato il device: %s\n", device);
if(stat(device, &file_infos) < 0){
printf("[TINY DRIVER] Attenzione: errore nell'accesso al device selezionato: %s, riprova con un altro dispositivo.\n", device);
return -1;
}
bzero((void *)&oldtio, sizeof(oldtio));
bzero((void *)&newtio, sizeof(newtio));
fd = open(device, O_RDWR | O_NONBLOCK | O_SYNC);
if (fd == -1) {
printf("[TINY SERIAL] failed to open /dev/ttytiny0 !\n");
return -1;
}
printf("[TINY SERIAL] succeed to open /dev/ttytiny0 !\n");
ret = tcgetattr(fd, &oldtio);
if (ret != 0) {
printf("[TINY SERIAL] failed to get attr !\n");
close(fd);
return -1;
}
set_termios(&newtio, B38400, PAR_EVEN, DATA_8BIT, STOP_2BIT);
tcflush(fd, TCIOFLUSH);
ret = tcsetattr(fd, TCSANOW, &newtio);
if (ret != 0) {
printf("[TINY SERIAL] failed to set termios !\n");
close(fd);
return -1;
}
tops.fd = fd;
tops.max_fd = tops.fd + 1;
FD_ZERO(&tops.rfds);
FD_ZERO(&tops.wfds);
FD_ZERO(&tops.efds);
FD_SET(tops.fd, &tops.rfds);
FD_SET(tops.fd, &tops.wfds);
FD_SET(tops.fd, &tops.efds);
if (tiny_write(&tops, str, strlen(str)) != 0) {
close(fd);
return -1;
}
if (tiny_read(&tops, buff, sizeof(buff)) != 0) {
close(fd);
return -1;
}
ret = tcsetattr(fd, TCSANOW, &oldtio);
if (ret != 0) {
printf("[TINY SERIAL] Errore nel reset delle impostazioni.\n");
}else{
printf("[TINY SERIAL] Impostazioni resettate correttamente.\n");
}
ret = tcflush(fd, TCIOFLUSH);
if(ret != 0){
printf("[TINY SERIAL] Errore nel flushing dei dati (finale) !\n");
}else{
printf("[TINY SERIAL] Dati flushati correttamente!\n");
}
//Program hangs on this call
ret = close(fd);
if (ret != 0) {
printf("[TINY SERIAL] Errore nella Chiusura del file !\n");
return -1;
}else{
printf("[TINY SERIAL] Chiusura del file avvenuta correttamente !\n");
}
return 0;
}
Masalah saya adalah: aplikasi pengujian berfungsi dengan baik (saya dapat menulis dan membaca string saya sendiri) hingga mencapai close(2): hang selamanya, tanpa mencapai akhir program (saya harus menutupnya dengan CTRL+C, dan kemudian menutup dengan benar saat modul kernel memanggil fungsi tiny_shutdown()).
Saya juga mencoba menulis program sederhana untuk membuka dan menutup perangkat /dev/ttytiny0, tetapi hasilnya sama (walaupun saya perhatikan, jika saya menghapus close(2) operasi, program masih hang tanpa berhenti, tetapi kali ini saya tidak bisa (jangan ditutup dengan CTRL+C).
Tautan/referensi apa pun ke buku atau materi tentang subjek ini akan sangat dihargai.
Terima kasih sebelumnya!
Edit : Di sini laporkan pesan log kernel (saat menjalankan aplikasi pengujian)
[ 446.862221] tiny_ser: module verification failed: signature and/or required key missing - tainting kernel
[ 446.864143] tiny_serial: 6Tiny serial driver loaded
[ 486.715801] tiny_serial: tiny_startup()
[ 486.715812] tiny_serial: tiny_set_termios()
[ 486.715814] tiny_serial: - data bits = 8
[ 486.715816] tiny_serial: - parity = none
[ 486.715818] tiny_serial: - stop bits = 1
[ 486.715820] tiny_serial: - RTS/CTS is disabled
[ 486.715824] tiny_serial: tiny_set_mctrl()
[ 486.715853] tiny_serial: tiny_set_termios()
[ 486.715856] tiny_serial: - data bits = 8
[ 486.715857] tiny_serial: - parity = even
[ 486.715859] tiny_serial: - stop bits = 2
[ 486.715861] tiny_serial: - RTS/CTS is disabled
[ 486.715943] tiny_serial: tiny_start_tx()
[ 487.116105] tiny_serial: tiny_timer()
[ 487.116171] tiny_serial: tiny_tx_chars()
[ 487.116183] tiny_serial: wrote 0x42
[ 487.116189] tiny_serial: wrote 0x75
[ 487.116196] tiny_serial: wrote 0x6f
[ 487.116202] tiny_serial: wrote 0x6e
[ 487.116208] tiny_serial: wrote 0x61
[ 487.116214] tiny_serial: wrote 0x73
[ 487.116220] tiny_serial: wrote 0x65
[ 487.116226] tiny_serial: wrote 0x72
[ 487.116232] tiny_serial: wrote 0x61
[ 487.116238] tiny_serial: wrote 0x20
[ 487.116245] tiny_serial: wrote 0x64
[ 487.116251] tiny_serial: wrote 0x72
[ 487.116257] tiny_serial: wrote 0x69
[ 487.116263] tiny_serial: wrote 0x76
[ 487.116269] tiny_serial: wrote 0x65
[ 487.116275] tiny_serial: wrote 0x72
[ 487.116281] tiny_serial: wrote 0x21
[ 487.116287] tiny_serial: wrote 0x21
[ 487.116295] tiny_serial: tiny_stop_tx()
[ 487.116303] tiny_serial: read: 0x42
[ 487.116312] tiny_serial: read: 0x75
[ 487.116318] tiny_serial: read: 0x6f
[ 487.116324] tiny_serial: read: 0x6e
[ 487.116330] tiny_serial: read: 0x61
[ 487.116337] tiny_serial: read: 0x73
[ 487.116343] tiny_serial: read: 0x65
[ 487.116349] tiny_serial: read: 0x72
[ 487.116355] tiny_serial: read: 0x61
[ 487.116361] tiny_serial: read: 0x20
[ 487.116367] tiny_serial: read: 0x64
[ 487.116373] tiny_serial: read: 0x72
[ 487.116379] tiny_serial: read: 0x69
[ 487.116385] tiny_serial: read: 0x76
[ 487.116391] tiny_serial: read: 0x65
[ 487.116397] tiny_serial: read: 0x72
[ 487.116403] tiny_serial: read: 0x21
[ 487.116409] tiny_serial: read: 0x21
[ 487.116444] tiny_serial: push to user space !
[ 487.116565] tiny_serial: tiny_start_tx()
[ 487.116709] tiny_serial: tiny_set_termios()
[ 487.116713] tiny_serial: - data bits = 8
[ 487.116715] tiny_serial: - parity = none
[ 487.116717] tiny_serial: - stop bits = 1
[ 487.116719] tiny_serial: - RTS/CTS is disabled
[ 487.116746] tiny_serial: tiny_tx_empty()
[ 487.516021] tiny_serial: tiny_timer()
[ 487.516094] tiny_serial: tiny_tx_chars()
[ 487.516104] tiny_serial: tiny_stop_tx()
[ 487.915917] tiny_serial: tiny_timer()
[ 487.915960] tiny_serial: tiny_tx_chars()
[ 487.915971] tiny_serial: tiny_stop_tx()
[ 505.910955] tiny_serial: tiny_timer()
[ 505.910968] tiny_serial: tiny_tx_chars()
[ 505.910971] tiny_serial: tiny_stop_tx()
[ 506.455320] tiny_serial: tiny_timer()
[ 506.455332] tiny_serial: tiny_tx_chars()
[ 506.455335] tiny_serial: tiny_stop_tx()
[ 506.855344] tiny_serial: tiny_timer()
[ 506.855428] tiny_serial: tiny_tx_chars()
[ 506.855437] tiny_serial: tiny_stop_tx()
[ 507.255499] tiny_serial: tiny_timer()
[ 507.255563] tiny_serial: tiny_tx_chars()
[ 507.255572] tiny_serial: tiny_stop_tx()
[ 507.655342] tiny_serial: tiny_timer()
[ 507.655401] tiny_serial: tiny_tx_chars()
[ 507.655411] tiny_serial: tiny_stop_tx()
[ 507.755090] tiny_serial: tiny_stop_rx()
[ 507.755100] tiny_serial: tiny_tx_empty()
[ 507.755103] tiny_serial: tiny_set_mctrl()
[ 507.755105] tiny_serial: tiny_shutdown()
[ 507.755108] tiny_serial: tiny_shutdown() - after del_timer()
Perhatikan bahwa urutan tiny_stop_rx() - tiny_tx_empty() - tiny_set_mctrl() - tiny_shutdown() dipanggil ketika saya menghentikan eksekusi aplikasi pengujian dengan CTRL+C.
Pesan berulang lainnya (urutan tiny_timer() - tiny_tx_chars() - tiny_stop_tx()) dihasilkan oleh pengatur waktu yang memeriksa apakah ada sesuatu yang harus dikirim (tetapi antriannya kosong, sehingga tertidur lagi).
Tentu saja, log aplikasi pengujian menunjukkan perilaku yang benar, hingga mencapai tutup() fungsi.
close()
mulai memblokir? Jika ya, yang mana entri terakhir? - person alk   schedule 01.07.2018del_timer()
dan menguji ulang? - person alk   schedule 01.07.2018tx_empty(port)
Fungsi ini menguji apakah fifo pemancar dan shifter untuk port yang dijelaskan oleh 'port' kosong. [..] Jika port tidak mendukung operasi ini, maka port tersebut akan mengembalikanTIOCSER_TEMT
. - person alk   schedule 01.07.20180
daritx_empty()
yang artinya tidak kosong. - person alk   schedule 01.07.2018close()
jarang terjadi ... - person alk   schedule 01.07.2018