原创 UART驱动,从驱动原理到实际驱动

2011-9-2 19:33 6303 9 9 分类: 软件与OS

第五章  Linux文件系统与设备文件

5.1  Linux文件系统与设备驱动

20110902192603413.jpg

应用程序和VFS之间的接口是系统调用,而VFS与磁盘文件系统以及普通之间的接口是file_operations结构体成员函数。

在设备驱动程序设计中,一般而言,会关心fileinode这两个结构体

1file结构体

File结构体代表一个打开的文件,系统中每个打开的文件在内核空间中有一个关联的struct file

struct file {

      /*

       * fu_list becomes invalid after file_free is called and queued via

       * fu_rcuhead for RCU freeing

       */

      union {

             struct list_head    fu_list;

             struct rcu_head fu_rcuhead;

      } f_u;

      struct path           f_path;

#define f_dentry       f_path.dentry

#define f_vfsmnt      f_path.mnt

      const struct file_operations      *f_op;   /*和文件关联的操作*/

      atomic_long_t            f_count;

      unsigned int              f_flags;  /*文件标志,如O_RDONLYO_NONBLOCKO_SYNC*/

      fmode_t                     f_mode; /*文件读/写模式*/

      loff_t                   f_pos;  /*当前读写的位置*/

      struct fown_struct      f_owner;

      unsigned int        f_uid, f_gid;

      struct file_ra_state     f_ra;

 

      u64               f_version;

#ifdef CONFIG_SECURITY

      void                     *f_security;

#endif

      /* needed for tty driver, and maybe others */

      void                     *private_data;

 

#ifdef CONFIG_EPOLL

      /* Used by fs/eventpoll.c to link all the hooks to this file */

      struct list_head    f_ep_links;

      spinlock_t            f_ep_lock;

#endif /* #ifdef CONFIG_EPOLL */

      struct address_space  *f_mapping;

#ifdef CONFIG_DEBUG_WRITECOUNT

      unsigned long f_mnt_write_state;

#endif

};

2inode结构体

VFS inode包括文件访问权限、属主、组、大小、生成时间、访问时间、最后修改时间等信息、他是Linux文件系统的最基本单位、也是文件系统连接任何子目录、文件的桥梁。

struct inode {

      struct hlist_node i_hash;

      struct list_head    i_list;

      struct list_head    i_sb_list;

      struct list_head    i_dentry;

      unsigned long             i_ino;

      atomic_t              i_count;

      unsigned int        i_nlink;

      uid_t                    i_uid;

      gid_t                    i_gid;

      dev_t                   i_rdev;

      u64               i_version;

      loff_t                   i_size;

#ifdef __NEED_I_SIZE_ORDERED

      seqcount_t           i_size_seqcount;

#endif

      struct timespec           i_atime;

      struct timespec           i_mtime;

      struct timespec           i_ctime;

      unsigned int        i_blkbits;

      blkcnt_t        i_blocks;

      unsigned short          i_bytes;

      umode_t                     i_mode;

      spinlock_t            i_lock;   /* i_blocks, i_bytes, maybe i_size */

      struct mutex        i_mutex;

      struct rw_semaphore  i_alloc_sem;

      const struct inode_operations  *i_op;

      const struct file_operations      *i_fop;   /* former ->i_op->default_file_ops */

      struct super_block      *i_sb;

      struct file_lock    *i_flock;

      struct address_space  *i_mapping;

      struct address_space  i_data;

#ifdef CONFIG_QUOTA

      struct dquot         *i_dquot[MAXQUOTAS];

#endif

      struct list_head    i_devices;

      union {

             struct pipe_inode_info      *i_pipe;

             struct block_device    *i_bdev;

             struct cdev          *i_cdev;

      };

      int                 i_cindex;

 

      __u32                  i_generation;

 

#ifdef CONFIG_DNOTIFY

      unsigned long             i_dnotify_mask; /* Directory notify events */

      struct dnotify_struct  *i_dnotify; /* for directory notifications */

#endif

 

#ifdef CONFIG_INOTIFY

      struct list_head    inotify_watches; /* watches on this inode */

      struct mutex        inotify_mutex;    /* protects the watches list */

#endif

 

      unsigned long             i_state;

      unsigned long             dirtied_when;      /* jiffies of first dirtying */

 

      unsigned int        i_flags;

 

      atomic_t              i_writecount;

#ifdef CONFIG_SECURITY

      void                     *i_security;

#endif

      void                     *i_private; /* fs or device private pointer */

};

 

5.2  sysfs文件系统与Linux设备模型

Sysfs文件系统是一个虚拟文件系统,它把连接在系统上的设备和总线组织成一个分级的文件,它们可以由用户空间存取,其顶级目录包括blockdevicebusdriversclasspowerfireware

/sys/buspci子目录下,又会再分出driversdevices目录,而devices目录中的文件对/sys/devices目录中的文件的符号连接。

20110902192641257.jpg

Device_driverdevice分别表示驱动和设备,而这两者都必须依附于一种总线,因此包含struct bus_type指针。在Linux 内核中,设备和驱动是分开注册的。

 第六章  字符设备驱动

6.1字符设备驱动结构

6.1.1 cdev结构体

cdev结构体描述一个字符设备,结构体如下:

20110902192733714.jpg

获得主设备号和次设备号:20110902192808462.jpg

cdev_init():初始化cdev的成员,并建立cdevfile_operations之间的连接

cdev_alloc():用于动态申请一个cdev内存

cdev_add()函数和cdev_del()分别向系统添加和删除一个cdev

20110902192831799.jpg

6.1.2      file_operation结构体

file_operation结构体中的成员函数是字符设备驱动程序设计的主体内容。这些函数实际会在应用程序进行Linuxopen()write()read()close()等系统调用时最终调用。

20110902192914781.jpg

20110902192945749.jpg

20110902193016784.jpg

完成内核空间和用户空间内存拷贝的函数为:copy_from_user()copy_to_user().

20110902193045287.jpg

6.2  globalmem虚拟设备实例描述

Globalmem意味着“全局内存”,在globalmem字符设备驱动中会分配一片大笑为GLOBALMEM_SIZE的内存空间,并在驱动中提供针对该片内存的读写、控制和定位函数,以供用户空间的进程能通过Linux系统调用访问这篇内存。


 

第十二章  工程中的Linux设备驱动

在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等不依附于总线。故Linux发明了一种虚拟总线,称为platform总线,相应的设备称为platform_device,而驱动称为paltform_driver

struct platform_device {

       const char     * name;        /*设备名*/

       int          id;

       struct device dev;

       u32        num_resources;   /*设备所使用各类资源数量*/

       struct resource     * resource;   /*资源*/

};

 

struct platform_driver {

       int (*probe)(struct platform_device *);

       int (*remove)(struct platform_device *);

       void (*shutdown)(struct platform_device *);

       int (*suspend)(struct platform_device *, pm_message_t state);

       int (*suspend_late)(struct platform_device *, pm_message_t state);

       int (*resume_early)(struct platform_device *);

       int (*resume)(struct platform_device *);

       struct pm_ext_ops *pm;

       struct device_driver driver;

};

 

模块加载和卸载函数仅仅通过platform_driver_register(),platform_driver_unregister()函数进行platform_driver的注册和注销,而原先的注册和注销字符设备驱动核心移交到platform_driverprobe()remove()成员函数中。

 

Linux的设备驱动分层结构:

硬件的相关信息屏蔽在板文件platform_deviceplatform_data中。

第十四章 Linux终端设备驱动

一、终端设备驱动结构

Linux内核中的tty的层次结构如图:

20110902193120129.jpg

tty设备发送数据流程:tty核心将用户获取的将要发给一个tty设备的数据,tty核心将数据传递给tty线路规程驱动,接着数据被传递到tty驱动,tty驱动将数据转换为可以发送给硬件的格式。

二、tty设备驱动

Driver/char/tty_io.c定义了tty设备通用的file_operations结构体并实现了接口函数tty_register_driver()用于注册tty设备,他会利用fs/char_dev.c提供的接口函数注册字符设备,与具体设备对应的tty驱动将实现tty_driver结构体中的成员函数。同时tty_io.c也提供了tty_register_ldisc()接口函数用于注册线路规程。

20110902193141833.jpg

   struct tty_driver {

    int    magic;        /* magic number for this structure 该结构体的幻数*/

    struct kref kref;    /* Reference management reference 管理 */

    struct cdev cdev;

    struct module    *owner;

    const char    *driver_name;

    const char    *name;

    int    name_base;    /* offset of printed name */

    int    major;        /* major device number 主设备号*/

    int    minor_start;    /* start of minor device number 开始次设备号 */

    int    minor_num;    /* number of *possible* devices 可能的设备数量 */

    int    num;        /* number of devices allocated 被分配的设备数量 */

    short    type;        /* type of tty driver  tty驱动的类型*/

    short    subtype;    /* subtype of tty driver tty驱动的子类 */

    struct ktermios init_termios; /* Initial termios  初始的temios*/

    int    flags;        /* tty driver flags  tty驱动的标志*/

    struct proc_dir_entry *proc_entry; /* /proc fs entry  /proc入口*/

    struct tty_driver *other; /* only used for the PTY driver 仅对PTY驱动有用*/

 

    /*

     * Pointer to the tty data structures

     */

    struct tty_struct **ttys;

    struct ktermios **termios;

    struct ktermios **termios_locked;

    void *driver_state;

 

    /*

     * Driver methods

     */

 

    const struct tty_operations *ops;

    struct list_head tty_drivers;

};

其中:driver_name是驱动名字,出现在/proc/ttysysfs中,name是设备节点名。

Init_termios为出事线路设置,为一个termios结构体。Termios用于保存当前的线路设置,这些线路设置控制当前的波特率、数据大小、数据流控设置等。

/*temios用于保存当前的线路设置,这些线路设置当前波特率、数据大小、数据流控制*/

struct ktermios {

    tcflag_t c_iflag;        /* input mode flags */

    tcflag_t c_oflag;        /* output mode flags */

    tcflag_t c_cflag;        /* control mode flags */

    tcflag_t c_lflag;        /* local mode flags */

    cc_t c_line;            /* line discipline */

    cc_t c_cc[NCCS];        /* control characters */

    speed_t c_ispeed;        /* input speed */

    speed_t c_ospeed;        /* output speed */

};

 

tty_operation结构体中有一些列的函数,主要实现了open(),close(),write(),tiocmget(),tiocmset()等函数。

 

三、UART设备驱动

Linux已经在serial_core.c中实现了UART设备的通用tty驱动层(姑且称其为串口核心层),这样UART驱动的主要任务变成实现serial-core.c中定义的uart_xxx接口。

20110902193205456.jpg

 

1.uart_driver

Uart_driver包含了设备的驱动名、设备名、设备号等信息,它封转了tty_driver,使得递呈的UART驱动无须关心tty_driver.

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;

};

 

2.uart_port

Uart_port用于描述一个UART端口的I/O端口或I/O内存地址、FIFO大小、端口等信息。

struct uart_port {

    spinlock_t        lock;            /* port lock 端口锁*/

    unsigned long        iobase;            /* in/out[bwl] I/O端口基地址 */

    unsigned char __iomem    *membase;        /* read/write[bwl]  I/O内存地址*/

    unsigned int        irq;            /* irq number 终端号*/

    unsigned int        uartclk;        /* base uart clock UART时钟*/

    unsigned int        fifosize;        /* tx fifo size 传输的FIFO大小*/

    unsigned char        x_char;            /* xon/xoff char xon/xoff字符 */

    unsigned char        regshift;        /* reg offset shift  寄存器位移*/

    unsigned char        iotype;            /* io access style I/O内存类型 */

    unsigned char        unused1;

 

#define UPIO_PORT        (0)

#define UPIO_HUB6        (1)

#define UPIO_MEM        (2)

#define UPIO_MEM32        (3)

#define UPIO_AU            (4)            /* Au1x00 type IO */

#define UPIO_TSI        (5)            /* Tsi108/109 type IO */

#define UPIO_DWAPB        (6)            /* DesignWare APB UART */

#define UPIO_RM9000        (7)            /* RM9000 type IO */

 

    unsigned int        read_status_mask;    /* driver specific 驱动相关的*/

    unsigned int        ignore_status_mask;    /* driver specific  驱动相关的*/

    struct uart_info    *info;            /* pointer to parent info 指向parent信息 */

    struct uart_icount    icount;            /* statistics 计数*/

 

    struct console        *cons;            /* struct console, if any console 结构体*/

#ifdef CONFIG_SERIAL_CORE_CONSOLE

    unsigned long        sysrq;            /* sysrq timeout sysrq超时*/

#endif

 

    upf_t            flags;

 

#define UPF_FOURPORT        ((__force upf_t) (1 << 1))

#define UPF_SAK            ((__force upf_t) (1 << 2))

#define UPF_SPD_MASK        ((__force upf_t) (0x1030))

#define UPF_SPD_HI        ((__force upf_t) (0x0010))

#define UPF_SPD_VHI        ((__force upf_t) (0x0020))

#define UPF_SPD_CUST        ((__force upf_t) (0x0030))

#define UPF_SPD_SHI        ((__force upf_t) (0x1000))

#define UPF_SPD_WARP        ((__force upf_t) (0x1010))

#define UPF_SKIP_TEST        ((__force upf_t) (1 << 6))

#define UPF_AUTO_IRQ        ((__force upf_t) (1 << 7))

#define UPF_HARDPPS_CD        ((__force upf_t) (1 << 11))

#define UPF_LOW_LATENCY        ((__force upf_t) (1 << 13))

#define UPF_BUGGY_UART        ((__force upf_t) (1 << 14))

#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16))

#define UPF_CONS_FLOW        ((__force upf_t) (1 << 23))

#define UPF_SHARE_IRQ        ((__force upf_t) (1 << 24))

#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        ((__force upf_t) (0x17fff))

#define UPF_USR_MASK        ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

 

    unsigned int        mctrl;            /* current modem ctrl settings 目前的modem控制设置*/

    unsigned int        timeout;        /* character-based timeout 基于字符的超时*/

    unsigned int        type;            /* port type 端口类型*/

    const struct uart_ops    *ops;         /*UART操作集*/

    unsigned int        custom_divisor;

    unsigned int        line;            /* port index 端口索引*/

    resource_size_t        mapbase;        /* for ioremap ioremap后基地址*/

    struct device        *dev;            /* parent device parament设备 */

    unsigned char        hub6;            /* this should be in the 8250 driver */

    unsigned char        suspended;

    unsigned char        unused[2];

    void            *private_data;        /* generic platform data pointer */

};

 

3uart_ops

Uart_ops定义了针对UART的一些列操作,包括发送、接收及线路设置等。

serial_core.c中定义了tty_operations的实例,包含uart_open()uart_close()等。这些函数会借助uart_ops结构体中成员函数来完成具体的操作。例如,tty_operationsuart_send_xchar()成员函数利用uart_ops中的start_tx()send_xchar()成员函数的实例。

 

因此,一个串口驱动要完成的主要工作如下:

1)定义uart_driveruart_opsuart_port等结构体的实例并在适当的地方更具具体的硬件驱动情况初始化他们,当然具体设备xxx的驱动可以将这些结构体套在新定义的xxx_uart_driverxxx_uart_opsxxx_uart_port之内。

2)在模块初始化调用uart_register()uart_add_one_port()以注册UART驱动并添加端口,在模块卸载时调用uart_unregister_driver()uart_remove_one_port()以注销UART驱动以移除端口。

3)根据具体硬件的datasheetshixianuart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作。

 

S3C6410串口UART驱动:samsung.c的模块加载和卸载函数中,通过uart_register_driver()uart_unregister_driver()注册和注销了s3c24xx_uart_drvs3c6400.c是一个platform驱动。

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
9
关闭 站长推荐上一条 /3 下一条