Linux下的串口总线驱动一 linux pci总线驱动

版权所有,转载请说明weiqing1981127

一.系统理论

PC机南桥的LPC总线(LowPin Count并行总线,代替以前的ISA总线)上挂接了一个超级I/O模块,而UART是这个超级模块芯片组的一部分,这个UART通过RS232线程转换与串行端口相连。与RS232不同,RS485并不是标准的PC接口,但在嵌入式领域,会为了可靠通信而使用RS485,RS485使用差分信号,因此其传输距离可以达到数百米,而RS232传输距离仅数几米,在处理器一端,RS485接口是半双工的UART操作。

Linux包含如下几种终端设备:串行端口终端(/dev/ttySn)、伪终端(/dev/pty)、控制终端(/dev/tty)、控制台终端(/dev/ttyn,/dev/conslole)。串行端口终端使用的设备名为/dev/ttyS0,/dev/ttyS1等,对应的设备号为(4,0),(4,1)。通过查看/proc/tty/drivers文件可以知道什么类型的tty设备存在以及什么驱动被加载到内核,这个文件包括一个当前存在的不同tty驱动的列表,包括驱动名,缺省的节点名,驱动的主编号,驱动的次编号范围,以及tty驱动的类型。

I/O系统调用是从带有线路规程的TTYI/O核心开始,然后通过TTY层,最后到达UART驱动层。主要涉及串口内核配置、UART层内核代码、TTY层内核代码、线路规程内核代码、串口测试代码五个部分。

二.串口内核配置

对于Mini2440串口驱动,我想从配置开始讲起。在内核中Kconfig必须完成一层层调用,如果没有在上一个Kconfig中调用该层Kconfig,那么该层Kconfig中的内容不会在此出现。这种情况下,只有当该层的Kconfig被其他层调用,该层Kconfig中的内容才会被显示。所以我们找找drivers/serial/Kconfig在哪里被调用的呢?

在/drivers/char/kconfig中可以看到一行代码source"drivers/serial/Kconfig",那我们就到drivers/serial/Kconfig下看看

Samsung SoC serialsupport对应于samsung.oserial_core.o

configSERIAL_SAMSUNG

tristate "Samsung SoC serial support"

depends on ARM && PLAT_S3C

select SERIAL_CORE

Support for consoleon Samsung SoC serial port对应于控制台驱动

SamsungS3C2440/S3C2442 Serial port support对应于s3c2440.o

在/drivers/char/Makefile中可以看到

obj-y+= mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.otty_buffer.o tty_port.o

我们知道tty_io.on_tty.otty_ioctl.otty_ldisc.otty_buffer.otty_port.o已编入内核

自此,我们知道关于串口驱动,我们内核中被编译了s3c2440.osamsung.o serial_core.otty_io.o n_tty.otty_ioctl.otty_ldisc.otty_buffer.otty_port.o

我们对此进行分类,属于UART层的是s3c2440.osamsung.o;属于TTY层的是serial_core.o;属于线路规程的是tty_io.on_tty.otty_ioctl.otty_ldisc.otty_buffer.otty_port.o 。

好了,对于串口的地图我们已经分析好了,那我们就按照UART层,TTY层,线路规程一个个的逛逛吧。

三.UART层内核代码

我们先看看samsung.o的init代码吧,这里面完成了uart_driver的注册

static int __inits3c24xx_serial_modinit(void)

{

int ret;

ret =uart_register_driver(&s3c24xx_uart_drv);//注册uart_driver

if (ret < 0) {

printk(KERN_ERR "failed to register UART drivern");

return -1;

}

return 0;

}

static structuart_driver s3c24xx_uart_drv = {

.owner=THIS_MODULE,

.dev_name= "s3c2410_serial",//设备名

.nr= CONFIG_SERIAL_SAMSUNG_UARTS,//UART端口个数

.cons= S3C24XX_SERIAL_CONSOLE, //指向控制台结构

.driver_name =S3C24XX_SERIAL_NAME,//驱动的名字

.major= S3C24XX_SERIAL_MAJOR,//串口主设备号

.minor= S3C24XX_SERIAL_MINOR,//串口次设备号

};

我们关注下上面这个结构体中一个成员S3C24XX_SERIAL_CONSOLE

#defineS3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console

Linux下的串口总线驱动(一) linux pci总线驱动

static structconsole s3c24xx_serial_console = {

.name= S3C24XX_SERIAL_NAME,

.device= uart_console_device,

.flags= CON_PRINTBUFFER,

.index= -1,

.write= s3c24xx_serial_console_write,

.setup= s3c24xx_serial_console_setup

};

上面是控制台的结构体成员。

对于UART驱动,我们除了需要注册uart_driver外,还需要注册端口,我们看看s3c2440.o。

这个文件里面注册了一个平台设备,其中平台设备的探测函数最终调用了samsung.o中的s3c24xx_serial_probe函数。

ints3c24xx_serial_probe(struct platform_device *dev,

struct s3c24xx_uart_info *info)

{

struct s3c24xx_uart_port *ourport;

int ret;

dbg("s3c24xx_serial_probe(%p, %p) %dn", dev, info,probe_index);

ourport = &s3c24xx_serial_ports[probe_index];//选择s3c24xx_uart_port

probe_index++; //索引号自增

dbg("%s: initialising port %p...n", __func__, ourport);

ret = s3c24xx_serial_init_port(ourport, info,dev); //初始化串口

if (ret < 0)

goto probe_err;

dbg("%s: adding portn", __func__);

uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);//向内核注册端口

platform_set_drvdata(dev, &ourport->port);//设置私有数据

ret = device_create_file(&dev->dev,&dev_attr_clock_source); //添加设备属性

if (ret < 0)

printk(KERN_ERR "%s: failed to add clksrc attr.n",__func__);

ret = s3c24xx_serial_cpufreq_register(ourport);//注册CPU频率

if (ret < 0)

dev_err(&dev->dev, "failed to add cpufreqnotifiern");

return 0;

probe_err:

return ret;

}

通过上面的函数,我们发现在UART层,我们调用了uart_add_one_port函数完成端口的添加,我们来看看添加了什么端口呢?

static structs3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS]= {

[0] = {

.port = {

.lock=__SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),

.iotype=UPIO_MEM,

.irq= IRQ_S3CUART_RX0,

.uartclk = 0,

.fifosize = 16,

.ops=&s3c24xx_serial_ops,//对UART操作的函数

.flags= UPF_BOOT_AUTOCONF,

.line= 0,

}

},

[1] = {

.port = {

.lock=__SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),

.iotype= UPIO_MEM,

.irq= IRQ_S3CUART_RX1,

.uartclk = 0,

.fifosize = 16,

.ops= &s3c24xx_serial_ops,//对UART操作的函数

.flags= UPF_BOOT_AUTOCONF,

.line= 1,

}

},

#ifCONFIG_SERIAL_SAMSUNG_UARTS > 2

[2] = {

.port = {

.lock=__SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),

.iotype= UPIO_MEM,

.irq= IRQ_S3CUART_RX2,

.uartclk = 0,

.fifosize = 16,

.ops=&s3c24xx_serial_ops,//对UART操作的函数

.flags= UPF_BOOT_AUTOCONF,

.line= 2,

}

},

#endif

#ifCONFIG_SERIAL_SAMSUNG_UARTS > 3

[3] = {

.port = {

.lock=__SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),

.iotype= UPIO_MEM,

.irq= IRQ_S3CUART_RX3,

.uartclk = 0,

.fifosize = 16,

.ops=&s3c24xx_serial_ops,//对UART操作的函数

.flags= UPF_BOOT_AUTOCONF,

.line= 3,

}

}

#endif

};

在端口的定义中,我们知道s3c24xx_uart_port中定义了一个uart_port结构体,继续跟踪对UART的操作函数

static structuart_ops s3c24xx_serial_ops = {

.pm= s3c24xx_serial_pm,

.tx_empty= s3c24xx_serial_tx_empty, //发送是否忙

.get_mctrl= s3c24xx_serial_get_mctrl,

.set_mctrl= s3c24xx_serial_set_mctrl,

.stop_tx =s3c24xx_serial_stop_tx,

.start_tx =s3c24xx_serial_start_tx,//类似于write

.stop_rx =s3c24xx_serial_stop_rx,

.enable_ms= s3c24xx_serial_enable_ms,

.break_ctl= s3c24xx_serial_break_ctl,

.startup =s3c24xx_serial_startup, //类似于open

.shutdown= s3c24xx_serial_shutdown,//类似于close

.set_termios= s3c24xx_serial_set_termios, //设置线路规程

.type= s3c24xx_serial_type,

.release_port =s3c24xx_serial_release_port, //释放端口资源

.request_port =s3c24xx_serial_request_port, //申请端口资源

.config_port= s3c24xx_serial_config_port, //配置端口

.verify_port= s3c24xx_serial_verify_port,

};

对于上述uart_ops函数,我们需要自己去实现uart层的具体操作。

我们在UART层主要涉及uart_driver,uart_port,uart_ops三个结构体,并调用tty层的uart_register_driver和uart_add_one_port完成驱动和端口的注册,UART层具体操作函数需要用户自己设计。

好了,总结下UART驱动层需要完成的任务:

其一,定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备XXX的驱动可以将这些结构套在新定义的XXX_uart_driver、XXX_uart_ops、XXX_uart_port之内。

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

其三,根据具体硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作。

  

爱华网本文地址 » http://www.aihuau.com/a/25101017/364782.html

更多阅读

串口通信的分类 基于fpga的串口通信

串口通信的分类串口通信可以分为同步通信和异步通信两类。同步通信是按照软件识别同步字符来实现数据的发送和接收,异步通信是一种利用字符的再同步技术的通信方式。2.1同步通信同步通信是一种连续串行传送数据的通信方式,一次通信

圣安德烈旗下的鱼龙——俄国战列舰篇(一) 圣安德烈级战列舰

从蒸汽机铁甲舰开始:1.彼得大帝号:毛子装甲舰的开山之作,1869年在卡列尔尼岛工厂开工建造,由A·A·波波夫中将主持设计,是俄国历史上第一艘万吨级远洋炮塔舰,该舰于1876年完工后加入波罗的海舰队。其设计布局类似英国“蹂躏”号(1873年完

cc2531USBdongle实现MT模式数据转发串口 stm32f103双串口转发

由于项目需求要实现CC2531USBdongle的MT模式来实现dongle的数据转发功能,框架简图1所示。PC端实现了MT模式,也可以用Ztool。总结起来就是dongle在MT模式下接收串口数据(数据满足MT格式),然后将数据解析为具体方法,接收到node端发送的数据

声明:《Linux下的串口总线驱动一 linux pci总线驱动》为网友简遇而安分享!如侵犯到您的合法权益请联系我们删除