linux uart

UART driver

 

LINUX

虽然UART驱动程序也是字符驱动程序,但它们并不像之前的通常的字符驱动程序那样直接暴露给内核系统调用。相反,UART驱动程序提供服务给另一个内核层:tty层。I/O系统调用的旅程首先从顶层的线路规程开始,通过tty层,最后到达UART驱动程序层。

 

UART驱动程序围绕3个关键的数据机构展开。

这三个数据结构都定义于include/linux/serial_core.h中。

1.     特定UART相关的驱动程序结构 struct uart_driver :

struct uart_driver {

  struct module  *owner;          

  const char     *driver_name;    

  const char     *dev_name;       

  

  int             major;          

  int             minor;          

  

  struct tty_driver *tty_driver;  

};

结构体中每个域的注释解释了其作用。owner域的作用和前面章节中讨论的file_operations结构中owner域相同

2.      Struct uart_port

  uart_port结构。UART驱动拥有的每个端口,都存在uart_port结构的一个实例。

struct uart_port {

  spinlock_t     lock;             

  unsigned int   iobase;           

  unsigned char  __iomem *membase;

  unsigned int   irq;              

  unsigned int   uartclk;          

  unsigned char  fifosize;         

  unsigned char  x_char;           

  

};

 

3.      uart_ops结构。这个结构是每个UART驱动必须支持的物理硬件上可完成的操作的入口函数的集合

 

  struct uart_ops {

  uint (*tx_empty)(struct uart_port *);     

  void (*set_mctrl)(struct uart_port *,

                    unsigned int mctrl);    

  uint (*get_mctrl)(struct uart_port *);    

  void (*stop_tx)(struct uart_port *);      

  void (*start_tx)(struct uart_port *);     

 

  

  void (*shutdown)(struct uart_port *);     

  void (*set_termios)(struct uart_port *,

                      struct termios *new,

                      struct termios *old);

  

  void (*config_port)(struct uart_port *,

                      int);                 

  

};

 

UART驱动为了将自身和内核联系起来,必须完成两个重要的步骤:

1. 通过调用uart_register_driver(struct uart_driver *)向串口核心层注册。

2. 调用uart_add_one_port(struct uart_driver *, struct uart_port *)注册其支持的每个端口。如果你的串口硬件支持热插拔,探测到设备存在后,从入口点向内核注册。第10章“PCI”的清单10.4中的CardBus Modem驱动,就是串口设备热插拔的例子。需要注意的是一些驱动使用封装的注册函数serial8250_register_port(struct uart_port *),在其内部调用了uart_add_one_port()

 

初始化过程中,USB_UART驱动程序首先用uart_register_driver()向串行核心注册自身。

 

2. platform驱动。platform驱动使用platform_driver_register()将自身注册进平台中。结构体platform_driver亦定义于include/linux/platform_device.h中,代表了platform驱动:

struct platform_driver {

  int (*probe)(struct platform_device *);

  int (*remove)(struct platform_device *);

  

  

  struct device_driver driver;

};

关于platform设备和platform驱动更详细的文档可参考Documentation/driver-model/platform.txt。为了讨论简单,我们的例子驱动注册了platform设备和platform驱动。

在初始化过程中,首先USB_UART驱动用uart_register_driver()向串口核心层注册自身。初始化成功后,在/proc/tty/drivers中你将会发现以usb_uart开始的新行。其次,驱动用platform_device_register_simple()注册两个platform设备(每个USB_UART一个)。正如前面提到的,platform设备通常在电路板引导过程中注册。随后,驱动用platform_driver_register()注册platform驱动入口点(probe(), remove(),suspend(), and resume())。USB_UARTplatform驱动和前面的platform设备联系在一起,并有一个相应的名字(usb_uart)。在以上步骤完成后,你将会发现在sysfs下出现两个新目录,每一个和相应的USB_UART端口相对应:/sys/devices/platform/usb_uart.0/  /sys/devices/platform/usb_uart.1/

因为Linux设备层开始检测到和注册的USB_UART platform设备相匹配的platform驱动,它调用属于platform驱动的probe()入口点[]usb_uart_probe()),每个USB_UART一次。probe入口点用uart_add_one_port()添加相应的USB_UART端口。以上步骤会触发config_port()入口点(前面讨论的uart_ops结构的一部分)的调用:声明并映射USB_UART寄存器空间。

直到某个应用程序打开USB_UART端口,才会占用中断号。当应用程序关闭USB_UART端口时,中断号被释放。下表表示了驱动程序代码中声明和释放内存区域与中断号的整个过程。

Table 6.2. Claiming and Freeing Memory and IRQ Resources

Module Insert模块插入

usb_uart_init()

uart_register_driver()

usb_uart_probe()

uart_add_one_port()

usb_uart_config_port()

request_mem_region()

Module Unload模块卸载

usb_uart_exit()

usb_unregister_driver()

usb_uart_remove()

uart_remove_one_port()

usb_uart_release_port()

release_mem_region()

打开Open/dev/ttyUUX

usb_uart_startup()

request_irq()

 

 

 

 

关闭Close/dev/ttyUUX

usb_uart_shutdown()

free_irq()

 

 

 

 

 

在发送过程中,驱动收集和UART端口相关的循环缓冲区中的待发送数据。UART驱动的start_tx()的入口函数usb_uart_start_tx()可以看到,数据存放于port->info->xmit.buf[port->info->xmit.tail]

在接收过程中,驱动使用tty_insert_flip_char()tty_flip_buffer_push()将从USB_UART收到的数据推出至tty驱动。这些在收中断处理例程usb_uart_rxint()中完成的。读者可在读完下一节“TTY驱动”后,再重读此例程。

 

Linux 手机的usb_uart驱动程序

Code View:

#include <linux/console.h>

#include <linux/platform_device.h>

#include <linux/tty.h>

#include <linux/tty_flip.h>

#include <linux/serial_core.h>

#include <linux/serial.h>

#include <asm/irq.h>

#include <asm/io.h>

 

#define USB_UART_MAJOR        200  /* You’ve to get this assigned */

#define USB_UART_MINOR_START  70   /* Start minor numbering here */

#define USB_UART_PORTS        2    /* The phone has 2 USB_UARTs */

#define PORT_USB_UART         30   /* UART type. Add this to

                                      include/linux/serial_core.h */

 

/* Each USB_UART has a 3-byte register set consisting of

   UU_STATUS_REGISTER at offset 0, UU_READ_DATA_REGISTER at

   offset 1, and UU_WRITE_DATA_REGISTER at offset 2 as shown

   in Table 6.1 */

#define USB_UART1_BASE    0xe8000000 /* Memory base for USB_UART1 */

#define USB_UART2_BASE    0xe9000000 /* Memory base for USB_UART2 */

#define USB_UART_REGISTER_SPACE  0×3

 

/* Semantics of bits in the status register */

#define USB_UART_TX_FULL         0×20  /* TX FIFO is full */

#define USB_UART_RX_EMPTY        0×10  /* TX FIFO is empty */

#define USB_UART_STATUS          0x0F  /* Parity/frame/overruns? */

 

#define USB_UART1_IRQ            3     /* USB_UART1 IRQ */

#define USB_UART2_IRQ            4     /* USB_UART2 IRQ */

#define USB_UART_FIFO_SIZE       32    /* FIFO size */

#define USB_UART_CLK_FREQ        16000000

 

static struct uart_port usb_uart_port[]; /* Defined later on */

 

/* Write a character to the USB_UART port */

static void

usb_uart_putc(struct uart_port *port, unsigned char c)

{

  /* Wait until there is space in the TX FIFO of the USB_UART.

     Sense this by looking at the USB_UART_TX_FULL bit in the

     status register */

  while (__raw_readb(port->membase) & USB_UART_TX_FULL);

 

  /* Write the character to the data port*/

  __raw_writeb(c, (port->membase+1));

}

 

/* Read a character from the USB_UART */

static unsigned char

usb_uart_getc(struct uart_port *port)

{

  /* Wait until data is available in the RX_FIFO */

  while (__raw_readb(port->membase) & USB_UART_RX_EMPTY);

 

  /* Obtain the data */

  return(__raw_readb(port->membase+2));

}

 

/* Obtain USB_UART status */

static unsigned char

usb_uart_status(struct uart_port *port)

{

  return(__raw_readb(port->membase) & USB_UART_STATUS);

}

 

/*

 * Claim the memory region attached to USB_UART port. Called

 * when the driver adds a USB_UART port via uart_add_one_port().

 */

static int

usb_uart_request_port(struct uart_port *port)

{

  if (!request_mem_region(port->mapbase, USB_UART_REGISTER_SPACE,

                          "usb_uart")) {

    return -EBUSY;

  }

  return 0;

}

 

/* Release the memory region attached to a USB_UART port.

 * Called when the driver removes a USB_UART port via

 * uart_remove_one_port().

 */

static void

usb_uart_release_port(struct uart_port *port)

{

  release_mem_region(port->mapbase, USB_UART_REGISTER_SPACE);

}

 

/* * Configure USB_UART. Called when the driver adds a USB_UART port.

 */

static void

usb_uart_config_port(struct uart_port *port, int flags)

{

  if (flags & UART_CONFIG_TYPE && usb_uart_request_port(port) == 0)

  {

    port->type = PORT_USB_UART;

  }

}

 

/* Receive interrupt handler */

static irqreturn_t

usb_uart_rxint(int irq, void *dev_id)

{

  struct uart_port *port = (struct uart_port *) dev_id;

  struct tty_struct *tty = port->info->tty;

 

  unsigned int status, data;

  /* … */

  do {

    /* … */

    /* Read data */

    data   = usb_uart_getc(port);

    /* Normal, overrun, parity, frame error? */

    status = usb_uart_status(port);

    /* Dispatch to the tty layer */

    tty_insert_flip_char(tty, data, status);

    /* … */

  } while (more_chars_to_be_read()); /* More chars */

  /* … */

  tty_flip_buffer_push(tty);

 

  return IRQ_HANDLED;

}

/* Called when an application opens a USB_UART */

static int

usb_uart_startup(struct uart_port *port)

{

  int retval = 0;

  /* … */

  /* Request IRQ */

  if ((retval = request_irq(port->irq, usb_uart_rxint, 0,

                            "usb_uart", (void *)port))) {

    return retval;

  }

  /* … */

  return retval;

}

 

/* Called when an application closes a USB_UART */

static void

usb_uart_shutdown(struct uart_port *port)

{

  /* … */

  /* Free IRQ */

  free_irq(port->irq, port);

 

  /* Disable interrupts by writing to appropriate

     registers */

  /* … */

}

 

/* Set UART type to USB_UART */

static const char *

usb_uart_type(struct uart_port *port)

{

  return port->type == PORT_USB_UART ? "USB_UART" : NULL;

}

 

/* Start transmitting bytes */

static void

usb_uart_start_tx(struct uart_port *port)

{

  while (1) {

    /* Get the data from the UART circular buffer and

       write it to the USB_UART’s WRITE_DATA register */

    usb_uart_putc(port,

                  port->info->xmit.buf[port->info->xmit.tail]);

    /* Adjust the tail of the UART buffer */

    port->info->xmit.tail = (port->info->xmit.tail + 1) &

                            (UART_XMIT_SIZE – 1);

    /* Statistics */

    port->icount.tx++;

    /* Finish if no more data available in the UART buffer */

    if (uart_circ_empty(&port->info->xmit)) break;

  }

  /* … */

}

 

/* The UART operations structure */

static struct    uart_ops    usb_uart_ops = {

  .start_tx     = usb_uart_start_tx,    /* Start transmitting */

  .startup      = usb_uart_startup,     /* App opens USB_UART */

  .shutdown     = usb_uart_shutdown,    /* App closes USB_UART */

  .type         = usb_uart_type,        /* Set UART type */

  .config_port  = usb_uart_config_port, /* Configure when driver

                                           adds a USB_UART port */

  .request_port = usb_uart_request_port,/* Claim resources

                                           associated with a

                                           USB_UART port */

  .release_port = usb_uart_release_port,/* Release resources

                                           associated with a

                                           USB_UART port */

#if 0    /* Left unimplemented for the USB_UART */

  .tx_empty     = usb_uart_tx_empty,    /* Transmitter busy? */

  .set_mctrl    = usb_uart_set_mctrl,   /* Set modem control */

  .get_mctrl    = usb_uart_get_mctrl,   /* Get modem control

  .stop_tx      = usb_uart_stop_tx,     /* Stop transmission */

  .stop_rx      = usb_uart_stop_rx,     /* Stop reception */

  .enable_ms    = usb_uart_enable_ms,   /* Enable modem status

                                           signals */

  .set_termios  = usb_uart_set_termios, /* Set termios */

#endif

};

 

static struct uart_driver usb_uart_reg = {

  .owner          = THIS_MODULE,          /* Owner */

  .driver_name    = "usb_uart",           /* Driver name */

  .dev_name       = "ttyUU",              /* Node name */

  .major          = USB_UART_MAJOR,       /* Major number */

  .minor          = USB_UART_MINOR_START, /* Minor number start */

  .nr             = USB_UART_PORTS,       /* Number of UART ports */

  .cons           = &usb_uart_console,    /* Pointer to the console

                                             structure. Discussed in Chapter

                                             12, "Video Drivers" */

};

 

/* Called when the platform driver is unregistered */

static int

usb_uart_remove(struct platform_device *dev)

{

  platform_set_drvdata(dev, NULL);

 

  /* Remove the USB_UART port from the serial core */

  uart_remove_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);

  return 0;

}

 

/* Suspend power management event */

static int

usb_uart_suspend(struct platform_device *dev, pm_message_t state)

{

  uart_suspend_port(&usb_uart_reg, &usb_uart_port[dev->id]);

  return 0;

}

 

/* Resume after a previous suspend */

static int

usb_uart_resume(struct platform_device *dev)

{

  uart_resume_port(&usb_uart_reg, &usb_uart_port[dev->id]);

  return 0;

}

 

/* Parameters of each supported USB_UART port */

static struct uart_port usb_uart_port[] = {

  {

    .mapbase  = (unsigned int) USB_UART1_BASE,

    .iotype   = UPIO_MEM,           /* Memory mapped */

    .irq      = USB_UART1_IRQ,      /* IRQ */

    .uartclk  = USB_UART_CLK_FREQ,  /* Clock HZ */

    .fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */

    .ops      = &usb_uart_ops,      /* UART operations */

    .flags    = UPF_BOOT_AUTOCONF,  /* UART port flag */

    .line     = 0,                  /* UART port number */

  },

  {

    .mapbase  = (unsigned int)USB_UART2_BASE,

    .iotype   = UPIO_MEM,           /* Memory mapped */

    .irq      = USB_UART2_IRQ,      /* IRQ */

    .uartclk  = USB_UART_CLK_FREQ,  /* CLock HZ */

    .fifosize = USB_UART_FIFO_SIZE, /* Size of the FIFO */

    .ops      = &usb_uart_ops,      /* UART operations */

    .flags    = UPF_BOOT_AUTOCONF,  /* UART port flag */

    .line     = 1,                  /* UART port number */

  }

};

 

/* Platform driver probe */

static int __init

usb_uart_probe(struct platform_device *dev)

{

  /* … */

 

  /* Add a USB_UART port. This function also registers this device

     with the tty layer and triggers invocation of the config_port()

     entry point */

  uart_add_one_port(&usb_uart_reg, &usb_uart_port[dev->id]);

  platform_set_drvdata(dev, &usb_uart_port[dev->id]);

  return 0;

}

 

struct platform_device *usb_uart_plat_device1; /* Platform device

                                                  for USB_UART 1 */

struct platform_device *usb_uart_plat_device2; /* Platform device

                                                  for USB_UART 2 */

 

static struct platform_driver usb_uart_driver = {

  .probe   = usb_uart_probe,            /* Probe method */

  .remove  = __exit_p(usb_uart_remove), /* Detach method */

  .suspend = usb_uart_suspend,          /* Power suspend */

  .resume  = usb_uart_resume,           /* Resume after a suspend */

  .driver  = {

     .name = "usb_uart",                /* Driver name */

  },

};

/* Driver Initialization */

static int __init

usb_uart_init(void)

{

   int retval;

 

   /* Register the USB_UART driver with the serial core */

   if ((retval = uart_register_driver(&usb_uart_reg))) {

     return retval;

   }

 

   /* Register platform device for USB_UART 1. Usually called

      during architecture-specific setup */

   usb_uart_plat_device1 =

      platform_device_register_simple("usb_uart", 0, NULL, 0);

   if (IS_ERR(usb_uart_plat_device1)) {

     uart_unregister_driver(&usb_uart_reg);

     return PTR_ERR(usb_uart_plat_device1);

   }

 

   /* Register platform device for USB_UART 2. Usually called

      during architecture-specific setup */

   usb_uart_plat_device2 =

     platform_device_register_simple("usb_uart", 1, NULL, 0);

   if (IS_ERR(usb_uart_plat_device2)) {

     uart_unregister_driver(&usb_uart_reg);

     platform_device_unregister(usb_uart_plat_device1);

     return PTR_ERR(usb_uart_plat_device2);

   }

 

   /* Announce a matching driver for the platform

      devices registered above */

   if ((retval = platform_driver_register(&usb_uart_driver))) {

     uart_unregister_driver(&usb_uart_reg);

     platform_device_unregister(usb_uart_plat_device1);

     platform_device_unregister(usb_uart_plat_device2);

   }

   return 0;

}

 

/* Driver Exit */

static void __exit

usb_uart_exit(void)

 

{

  /* The order of unregistration is important. Unregistering the

     UART driver before the platform driver will crash the system */

 

  /* Unregister the platform driver */

  platform_driver_unregister(&usb_uart_driver);

 

  /* Unregister the platform devices */

  platform_device_unregister(usb_uart_plat_device1);

  platform_device_unregister(usb_uart_plat_device2);

 

  /* Unregister the USB_UART driver */

  uart_unregister_driver(&usb_uart_reg);

}

 

module_init(usb_uart_init);

module_exit(usb_uart_exit);

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值