Linux驱动学习笔记(四)

终端控制台相关概念

在Linux中,TTY(终端)是一类字符设备的统称,包括三类:控制台、串口、伪终端。

控制台是一个虚拟的终端,必须映射到真正的终端上;可以简单的理解为printk输出的地方;只输出不输入,中能在内核中访问。

伪终端由主从两个成对的设备构成。输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主设备的输出,形成双向管道。


Linux内核中使用uart_driver描述串口驱动,它包含串口设备的驱动名、设备名、设备号等等:

struct uart_driver{

     srtuct module*pwner;

     const char*driver_name;//驱动名

     const char*dev_name;//设备名

     int major;//主设备号

     int minor;//起始次设备号

     int nr;//设备数

     struct console*cons;

     structuart_state *State;

     structtty_driver *tty_driver;

};

串口设备驱动注册:

int uart_register_driver(struct uart_driver *drv);

端口描述结构体:

uart_port用于描述一个UART端口的地址、FIFO大小、端口类型等信息;

struct_port{

     spinlock_tlock;//端口锁

     unsigned intiobase;//IO端口基地址

unsigned char __iomem *membase;//IO内存基地址

unsigned int irq;//中断号

unsigned char fifosize;//传输fifo大小

const struct uart_ops *ops;

};

uart_ops操作函数集合:

unsigned int  (*tx_empty)(structuart_port *);

     void     (*set_mctrl)(struct uart_port *, unsignedint mctrl);

     unsigned int  (*get_mctrl)(struct uart_port *);

     void     (*stop_tx)(struct uart_port *);

     void     (*start_tx)(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 *, structktermios *new,struct ktermios *old);

     void     (*set_ldisc)(struct uart_port *, int new);

     void     (*pm)(struct uart_port *, unsigned intstate,unsigned int oldstate);

     int      (*set_wake)(struct uart_port *, unsignedint state);

     /*

      * Return a string describing the type of theport

      */

     const char*(*type)(struct uart_port *);

     /*

      * Release IO and memory resources used by theport.

      * This includes iounmap if necessary.

      */

     void     (*release_port)(struct uart_port *);

     /*

      * Request IO and memory resources used by theport.

      * This includes iomapping the port ifnecessary.

      */

     int      (*request_port)(struct uart_port *);

     void     (*config_port)(struct uart_port *, int);

     int      (*verify_port)(struct uart_port *, structserial_struct *);

     int      (*ioctl)(struct uart_port *, unsigned int,unsigned long);

     。。。。。。。。。。。。

};

添加端口:

int uart_add_one_port(struct uart_driver *reg, structuart_port *port);

串口驱动实现流程:

  • 定义uart_driver的变量,并初始化
  • 使用uart_register_driver来注册驱动
  • 初始化uart_port和uart_ops函数集合
  • 调用uart_add_one_port添加初始化好的uart_port

 

块设备驱动

块设备将数据存储在固定大小的块中,每个块的大小一般为512字节到32768字节之间。

块设备和字符设备的区别:

最大的区别是读写数据的基本单元不同,块设备读写数据的基本单元是块,而字符设备的基本单元是字节。块设备可以随机访问,而字符设备只能顺序访问。

Mapping Layer:

首先确定文件系统的block size,然后计算所请求的数据包含多少个block。调用具体文件系统的函数来访问文件的inode,确定所请求的数据在磁盘上的逻辑块地址。

Generic Block Layer:

Linux内核为块设备抽象了统一的模型,把块设备看做是由若干个扇区组成的数据空间。上层的读写请求在通用的块层被构造成一个或多个bio结构。

I/O Scheduler Layer:

I/O调度层负责将I/O操作进行排序。

块设备描述结构体:

struct gendisk {

     int major;             /* major number of driver */

     intfirst_minor;//次设备号

     intminors;    /* maximum number of minors,=1 for* disks that can't be partitioned. */

     chardisk_name[DISK_NAME_LEN];   /* name ofmajor driver */

     const structblock_device_operations *fops;//块设备操作函数集合

    structrequest_queue *queue;//请求队列

    ..........................................

     int node_id;

};

注册块设备:

void add_disk(struct gendisk *disk);

I/O请求:

使用struct request;来表示等待处理的块设备I/0请求。

struct request{

     structlist_head queuelist;//链表结构

     sector_tsector;//要操作的首个扇区

     unsigned longnr_sectors;//要操作的扇区数目

     struct bio*bio;//请求bio结构体的链表

     struct bio*biotail;//请求的bio结构体的链表尾

};

请求队列:

IO请求request所形成的队列。Linux中struct request_queue描述。

初始化请求队列,一般在块设备驱动的模块加载函数中调用。

struct request_queue *blk_init_queue(request_fn_proc*rfn,spinlock_t *lock);

清除请求队列,这函数完成将请求队列返回给系统的任务,一般在快设备驱动模块卸载函数中调用。

void blk_cleanup_queue(request_queue_t *q);

得到下一个请求:

struct request *elv_next_request(request_queue_t *queue);

从队列中删除一个请求:

void blkdev_dequeue_request(struct request *req);

分配请求队列,对于FLASH、RAM盘等完全随机访问的非机械设备,并不需要进行复杂的I/O调度,这个时候,应该使用下述函数分配1个请求队列

request_queue_t *blk_alloc_queue(int gfp_mask);

绑定“请求队列”和“制造请求”函数。

void blk_queue_make_request(request_queue_t*q,make_request_fn *mfn)

LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)判断内核版本是否小于2.6.24

猜你喜欢

转载自blog.csdn.net/qq_35699583/article/details/80902820