由于没有经过完整测试代码中可能会有不足之出,如有网友发现还请斧正
这是一个学习文档,所有代码仅供学习使用,请勿在生产环境中使用
代码在ubuntu 18.04下经过测试
- [邮箱] :(liulf_pc@126.com)
- [CSDN] :(https://blog.csdn.net/qqstring)
- [仓库地址] :(https://gitee.com/stringliulf/char_driver.git)
- [简书] :(https://www.jianshu.com/u/752603033847)
tty驱动
tty驱动是一个应用很广泛的一类驱动,对于理解驱动模型也有很好的帮助,在此写下一个简单的tty设备驱动用于测试
完成一个很简单的功能,类似与一个回环驱动,写入什么数据就返回什么数据,实际驱动中修改相应的发送和接收函数即可
测试代码
测试代码很简单,类似一个串口测试程序,open read write close四步曲
打开设备,设置设备,写入数据,读取数据
#define max_buffer_size 100 /*recv buffer size */
int open_serial(int k)
{
int ret;
ret = open("/dev/ttytiny", O_RDWR | O_NOCTTY);
if (ret == -1) {
perror("open error");
exit(-1);
} else
printf("Open %s success\n", pathname);
return ret;
}
int main()
{
int fd;
ssize_t n;
char recv[max_buffer_size] = { 0 };
struct termios opt;
char write_buf[] = "abcdefghijklmnopqrstuvwxyz1234567890";
fd = open_serial(0); /*open device 0 */
tcgetattr(fd, &opt);
cfmakeraw(&opt);
tcsetattr(fd, TCSANOW, &opt);
n = write(fd, write_buf, sizeof write_buf);
printf("write ret:%ld\n", n);
printf("ready for receiving data...\n");
n = read(fd, recv, sizeof(recv));
if (n == -1) {
perror("read error");
exit(-1);
}
printf("data receive:%ld\n", n);
printf("The data received is %s\n", recv);
if (close(fd) == -1)
perror("close error");
return 0;
}
驱动代码
由于驱动代码有点多,这里只留下了关键的代码,具体代码可以参考代码库中的文件
代码没有做一些出错处理,实际应用中请增加相关容错代码
代码中设计了一个fifo,写入的时候将数据 复制到fifo,读取的时候从fifo中读取相应的数据
设计一个timer在写入的时候启动timer,在timer的回调函数中将数据写入tty的缓冲区,在早期的内核版本中timer的设置方式可能不一样,该代码在ubuntu 18.04的内核进行编写测试
在tty驱动中没有read函数,通过flip将数据压入tty的buffer
struct tiny_tty {
struct tty_port port;
struct kfifo fifo;
int index;
struct timer_list timer;
};
struct tiny_tty tty0;
struct tty_driver *tiny_tty_driver;
static int tiny_write(struct tty_struct *tty, const unsigned char *buf,
int count)
{
int ret = kfifo_in(&tty0.fifo, buf, count);
mod_timer(&tty0.timer, jiffies + HZ / 100);
return ret;
}
static const struct tty_operations tiny_ops = {
.open = tiny_open,
.close = tiny_close,
.write = tiny_write,
.write_room = tiny_write_room,
};
void callback(struct timer_list *timer_list)
{
struct tiny_tty *t;
char buf[100];
int cnt = 0;
t = container_of(timer_list, struct tiny_tty, timer);
cnt = kfifo_out(&t->fifo, buf, sizeof buf);
if (cnt > 0) {
printk(KERN_INFO "%s flip\n", __func__);
tty_insert_flip_string(&t->port, buf, cnt);
tty_flip_buffer_push(&t->port);
}
}
static int __init tiny_tty_init(void)
{
int result;
printk(KERN_ALERT "TINY device init\n");
tiny_tty_driver = alloc_tty_driver(1);
if (!tiny_tty_driver)
return -ENOMEM;
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "usbtiny";
tiny_tty_driver->name = "ttytiny";/*在dev目录下产生的设备文件名*/
tiny_tty_driver->major = 0;
tiny_tty_driver->minor_start = 0;
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL;
tiny_tty_driver->flags =
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV |
TTY_DRIVER_UNNUMBERED_NODE;/*这个设置只产生一个设备 即ttytiny 不产生类似于 ttyusb0的序列设备,该区动也只能注册一个ttytiny设备*/
tiny_tty_driver->init_termios = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver, &tiny_ops);
result = tty_register_driver(tiny_tty_driver);
tty0.index = 0;
tty_port_init(&(tty0.port));
tty_port_register_device(&tty0.port, tiny_tty_driver, tty0.index, NULL);
timer_setup(&tty0.timer, callback, 1);
result = kfifo_alloc(&tty0.fifo, 1024, GFP_KERNEL);
if (result != 0) {
pr_err("kfifo_alloc error:%d\n", result);
}
return result;
}
static void __exit tiny_tty_exit(void)
{
/* 删除 timer */
del_timer(&tty0.timer);
printk(KERN_ALERT "del_timer \n");
/* 删除device */
tty_unregister_device(tiny_tty_driver, tty0.index);
printk(KERN_ALERT "tty_unregister_device \n");
/* 删除driver */
tty_unregister_driver(tiny_tty_driver);
printk(KERN_ALERT "TINY device tty_unregister_driver\n");
}
- [邮箱] :(liulf_pc@126.com)
- [简书] :(https://www.jianshu.com/u/752603033847)
- [CSDN] :(https://blog.csdn.net/qqstring)
- [仓库地址] :(https://gitee.com/stringliulf/char_driver.git)