linux驱动移植-uart设备驱动

----------------------------------------------------------------------------------------------------------------------------内核版本:linux 5.2.8根文件系统:busybox 1.25.0u-boot:2016.05----------------------------------------------------------------------------------------------------------------------------

一、UART驱动框架

UART设备驱动框架如下图所示,下图是来自其他博客,仅供参考:

上图展示了四种注册UART设备驱动的方式,分别为case A到case D,这张图主要由4个部分组成:

  • character device:这里指的就是tty字符设备节点,比如/dev/ttyN;
  • tty_register_device:用于注册tty字符设备;
  • tty_driver:tty驱动的核心结构体,与之关联的数据结构有tty_struct、、tty_port、tty_operations、tty_ldisc、tty_ldisc_ops等,通过tty_register_driver将tty_driver注册到内核;
  • uart_driver:uart驱动的核心结构体,也是UART驱动需要实现的部分,与之关联的数据结构有uart_state、uart_port、uart_ops等,通过uart_register_driver将uart_driver注册到内核即可;

二、UART核心数据结构

学习uart驱动,首先要了解驱动框架涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。

2.1 struct uart_driver

每一款SoC的UART都需要去实现并定义个属于它自己uart_driver结构,uart_driver包含了串口设备名,串口驱动名,主次设备号,串口控制台(可选))等信息,还封装了tty_driver,定义在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:串口设备的名称;
  • major:主设备号;
  • minor:次设备号;
  • nr:该驱动支持的串口数量;比如,某某芯片的介绍:”多达3个UART接口“,那么这个nr指的就是这个3;
  • cons:其对应的console,若该uart_driver支持serial console;否则为NULL;
  • state:下层,串口驱动层;指向一个数组,对应了芯片的每一个UART(比如 uart_state[0] 对应了芯片的 UART0以此类推),需要初始化为NULL;
  • tty_driver:上层,tty驱动层;需要初始化为NULL;、
2.2 struct console

struct console用于实现控制台打印功能,定义在include/linux/console.h:

struct console {
        char    name[16];
        void    (*write)(struct console *, const char *, unsigned);
        int     (*read)(struct console *, char *, unsigned);
        struct tty_driver *(*device)(struct console *, int *);
        void    (*unblank)(void);
        int     (*setup)(struct console *, char *);
        int     (*match)(struct console *, char *name, int idx, char *options);
        short   flags;
        short   index;
        int     cflag;
        void    *data;
        struct   console *next;
};

其中部分参数含义如下:

  • name:控制台名称;
  • write:写函数;
  • read:读函数;
  • device:控制台设备;
  • unblank:
  • setup:初始化函数, 以串口驱动控制台为例,函数内容设置串口波特率、发送、接收等功能 ;
  • match:
  • flags:标志位;
  • index:索引值;
  • pflag:
  • data:控制台数据,一以串口驱动控制台为例,存储串口驱动uart_driver;
  • next:
2.3 struct uart_state

每一个uart端口对应着一个uart_state,该结构体将uart_port与对应的circ_buf联系起来。uart_state有两个成员在底层串口驱动会用到:xmit和port。

用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。

串口接收中断处理函数需要通过port将接收到的数据传递给线路规程层。定义在include/linux/serial_core.h:

/*
 * This is the state information which is persistent across opens.
 */
struct uart_state {
        struct tty_port         port;

        enum uart_pm_state      pm_state;
        struct circ_buf         xmit;

        atomic_t                refcount;
        wait_queue_head_t       remove_wait;
        struct uart_port        *uart_port;
};

其中部分参数含义如下:

  • port:tty port;
  • pm_state:
  • xmit:串口待发送数据环形缓冲区;
  • refcount:
  • remove_wait:
  • uart_port:uart port;
2.4 struct uart_port

uart_port存储的是与UART串口硬件相关的信息,需要芯片厂家定义自己的 uart_port 结构并填充它,该结构定义在include/linux/serial_core.h:

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_ldisc)(struct uart_port *,
                                             struct ktermios *);
        unsigned int            (*get_mctrl)(struct uart_port *);
        void                    (*set_mctrl)(struct uart_port *, unsigned int);
        unsigned int            (*get_divisor)(struct uart_port *,
                                               unsigned int baud,
                                               unsigned int *frac);
        void                    (*set_divisor)(struct uart_port *,
                                               unsigned int baud,
                                               unsigned int quot,
                                               unsigned int quot_frac);
        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);
        int                     (*iso7816_config)(struct uart_port *,
                                                  struct serial_iso7816 *iso7816);
        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           quirks;                 /* internal quirks */

#define UPIO_PORT               (SERIAL_IO_PORT)        /* 8b I/O port access */
#define UPIO_HUB6               (SERIAL_IO_HUB6)        /* Hub6 ISA card */
#define UPIO_MEM                (SERIAL_IO_MEM)         /* driver-specific */
#define UPIO_MEM32              (SERIAL_IO_MEM32)       /* 32b little endian */
#define UPIO_AU                 (SERIAL_IO_AU)          /* Au1x00 and RT288x type IO */
#define UPIO_TSI                (SERIAL_IO_TSI)         /* Tsi108/109 type IO */
#define UPIO_MEM32BE            (SERIAL_IO_MEM32BE)     /* 32b big endian */
#define UPIO_MEM16              (SERIAL_IO_MEM16)       /* 16b little endian */

        /* quirks must be updated while holding port mutex */
#define UPQ_NO_TXEN_TEST        BIT(0)

        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 */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
        unsigned long           sysrq;                  /* sysrq timeout */
        unsigned int            sysrq_ch;               /* char for sysrq */
#endif
        /*
         * These flags must be equivalent to the flags defined in
         * include/uapi/linux/tty_flags.h which are the userspace definitions
         * assigned from the serial_struct flags in uart_set_info()
         * [for bit definitions in the UPF_CHANGE_MASK]
         *
         * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable
         * The remaining bits are serial-core specific and not modifiable by
         * userspace.
         */
#define UPF_FOURPORT            ((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
#define UPF_SAK                 ((__force upf_t) ASYNC_SAK            /* 2  */ )
#define UPF_SPD_HI              ((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
#define UPF_SPD_VHI             ((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
#define UPF_SPD_CUST            ((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
#define UPF_SPD_WARP            ((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
#define UPF_SPD_MASK            ((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
#define UPF_SKIP_TEST           ((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
#define UPF_AUTO_IRQ            ((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
#define UPF_HARDPPS_CD          ((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
#define UPF_SPD_SHI             ((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
#define UPF_LOW_LATENCY         ((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
#define UPF_BUGGY_UART          ((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )

#define UPF_NO_THRE_TEST        ((__force upf_t) (1 << 19))
/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS            ((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS            ((__force upf_t) (1 << 21))
#define UPF_HARD_FLOW           ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW           ((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW           ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ           ((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR            ((__force upf_t) (1 << 25))
#define UPF_BUG_THRE            ((__force upf_t) (1 << 26))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE          ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF       ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT          ((__force upf_t) (1 << 29))
#define UPF_DEAD                ((__force upf_t) (1 << 30))
#define UPF_IOREMAP             ((__force upf_t) (1 << 31))

#define __UPF_CHANGE_MASK       0x17fff
#define UPF_CHANGE_MASK         ((__force upf_t) __UPF_CHANGE_MASK)
#define UPF_USR_MASK            ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

#if __UPF_CHANGE_MASK > ASYNC_FLAGS
#error Change mask not equivalent to userspace-visible bit defines
#endif

        /*
         * Must hold termios_rwsem, port mutex and port lock to change;
         * can hold any one lock to read.
         */
        upstat_t                status;

#define UPSTAT_CTS_ENABLE       ((__force upstat_t) (1 << 0))
#define UPSTAT_DCD_ENABLE       ((__force upstat_t) (1 << 1))
#define UPSTAT_AUTORTS          ((__force upstat_t) (1 << 2))
#define UPSTAT_AUTOCTS          ((__force upstat_t) (1 << 3))
#define UPSTAT_AUTOXOFF         ((__force upstat_t) (1 << 4))
#define UPSTAT_SYNC_FIFO        ((__force upstat_t) (1 << 5))

        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           unused[2];
        const char              *name;                  /* port name */
        struct attribute_group  *attr_group;            /* port specific attributes */
        const struct attribute_group **tty_groups;      /* all attributes (serial core use only) */
        struct serial_rs485     rs485;
        struct serial_iso7816   iso7816;
        void                    *private_data;          /* generic platform data pointer */
};

其中部分参数含义如下:

  • iobase:I/O端口寄存器基地址,物理地址;
  • membase:I/O端口寄存器基地址,虚拟地址;
  • irq:中断号,一般存放的是接收中断编号;
  • irqflags:中断标志;
  • uartclk:串口时钟;
  • fifosize:FIFO缓冲区大小;
  • regshift:寄存器位移;
  • iotype:I/O访问方式;
  • icount:串口信息计数器;
  • state:指向struct uart_state;
  • type:端口类型;
  • ops:串口端口的操作函数;
  • line:端口索引,通过drv->state + uport->line可以获取到当前UART对应的uart_state;
  • mapbase:I/O内存物理基地址;
  • dev:设备模型中的设备,一般存放的是platform device中device;
2.5 struct uart_ops

uart_ops定义了UART硬件相关相关的操作集,芯片厂家需要进行硬件寄存器级的适配其中各个操作,该结构定义在include/linux/serial_core.h:

/*
 * This structure describes all the operations that can be done on the
 * physical hardware.  See Documentation/serial/driver.rst for details.
 */
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);

        /*
         * 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.
         */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,我可以回答这个问题。 移植u-boot串口驱动的代码需要了解目标板的串口硬件及其寄存器的映射情况,以及u-boot串口驱动的框架。以下是一个示例代码,可以根据具体情况进行修改: ``` #include <common.h> #include <dm.h> #include <serial.h> static int my_serial_setbrg(struct udevice *dev, int baudrate) { // TODO: 设置波特率寄存器的值 return 0; } static int my_serial_probe(struct udevice *dev) { // TODO: 初始化串口硬件并打开串口 return 0; } static int my_serial_putc(struct udevice *dev, const char ch) { // TODO: 向串口发送一个字符 return 0; } static int my_serial_pending(struct udevice *dev, bool input) { // TODO: 查询串口接收缓冲区中的字符数或者查询发送缓冲区是否为空 return 0; } static int my_serial_getc(struct udevice *dev) { // TODO: 从串口接收一个字符 return 0; } static int my_serial_ofdata_to_platdata(struct udevice *dev) { // TODO: 从设备树中读取相关信息,如波特率 return 0; } static const struct dm_serial_ops my_serial_ops = { .setbrg = my_serial_setbrg, .putc = my_serial_putc, .pending = my_serial_pending, .getc = my_serial_getc, }; static const struct udevice_id my_serial_ids[] = { { .compatible = "my_serial", }, { } }; U_BOOT_DRIVER(my_serial) = { .name = "my_serial", .id = UCLASS_SERIAL, .of_match = my_serial_ids, .probe = my_serial_probe, .priv_auto_alloc_size = sizeof(struct dm_serial_priv), .ops = &my_serial_ops, .ofdata_to_platdata = my_serial_ofdata_to_platdata, }; ``` 以上代码中,需要填写TODO注释中的内容,以适配具体的硬件和需求。其中,`my_serial`是一个自定义的串口设备,在设备树中需要指定其`compatible`属性为`my_serial`。在编译时,需要将此代码编译进u-boot的image中,并在设备树中描述该串口设备。 ### 回答2: 移植UBOOT串口驱动的代码的过程如下: 1. 找到移植UBOOT的源码,并进入UART驱动对应的目录。 2. 根据目标硬件平台的串口硬件规格,修改串口驱动文件中的硬件参数。例如,调整串口设备地址、波特率、数据位、停止位和校验位等。 3. 配置串口驱动的引脚。根据硬件规格,确定相应引脚的功能和连接情况,并在板级配置文件中进行相应的配置。 4. 判断串口读取和写入的方式。根据串口硬件的特点,选择适当的读取和写入方式。可以选择轮询方式,也可以使用中断方式。 5. 实现串口初始化。根据目标平台的需求,实现串口初始化函数,包括配置波特率、数据位、停止位、校验位以及中断处理等。 6. 实现发送和接收数据的函数。编写串口发送数据函数和接收数据函数,用于发送指令或数据到串口以及从串口接收数据。 7. 编译和生成可执行文件。根据目标硬件平台和编译工具链的要求,进行编译和链接操作,生成可执行文件。根据需求,可以使用Makefile等工具进行编译自动化管理。 8. 测试驱动程序。将生成的驱动程序烧录到目标硬件上,并通过相应的串口工具或软件进行测试,验证驱动程序的正确性和稳定性。 9. 调试和优化。根据测试结果进行调试和优化,确保驱动程序的功能和性能达到预期要求。 最后,将移植完毕的UBOOT串口驱动代码提交到相应的版本控制系统中,以便后续维护和管理。 ### 回答3: 移植uboot串口驱动的代码涉及到uboot源码的修改和适配,并在其中添加新的串口驱动代码。具体步骤如下: 1. 首先,需要找到uboot源码中的串口驱动文件。一般情况下,uboot的串口驱动文件位于`drivers/serial`目录下。在该目录下,可以找到不同架构的串口驱动代码。 2. 打开对应架构的串口驱动文件,并进行移植适配。需要根据目标设备的硬件信息,修改和调整串口的寄存器地址和配置参数,确保其能够正确地操作对应的硬件串口。修改的关键点有:波特率设置、数据位设置、校验位设置、停止位设置等。这些设置通常通过对寄存器的配置来实现。 3. 在uboot中添加新的串口驱动文件。将移植适配好的串口驱动代码拷贝到`drivers/serial`目录下,并在Makefile文件中添加相关的编译选项,确保可以将新的串口驱动代码编译进uboot。 4. 修改uboot的全局配置文件`include/configs`,使其能够使用新的串口驱动。在该文件中,找到对应目标设备的配置项,并将其串口驱动设置为使用新的驱动名称。 5. 编译uboot源码。使用交叉编译工具链编译uboot,并生成最终的uboot镜像文件。 6. 将生成的uboot镜像文件烧录到目标设备中,启动设备,并通过串口调试工具查看串口输出是否正常。 以上就是移植uboot串口驱动的基本步骤,具体的实施过程中还需要根据不同的硬件架构和驱动接口进行相应的调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值