One-Time Vendor

本文介绍了一次性供应商(One-Time Vendor)的概念及其在SAP系统中的应用。一次性供应商用于偶尔或仅一次交易的情况,以减少不必要的个体供应商记录创建。在创建一次性供应商时,需指定一次性账户组,以抑制特定供应商的数据录入,直到创建采购文件(如采购订单)时才需输入供应商地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SCN上有个帖子,说明了one-time vendor的概念,现转载在此,以作学习。

http://forums.sdn.sap.com/thread.jspa?messageID=8154128&tstart=0#8154128

T-code:XK01, the same to normal vendor.

You can create special vendor master records for vendors from whom you procure goods only once or rarely, so-called One-Time Vendor Master Records.

For example, suppose you order goods from a vendor with whom you usually do not place orders, because your main vendor was not able to supply the required items. In this case, you would use a "one-time vendor" master record.

In contrast to other master records, a "one-time vendor" master record is used for several vendors. The purpose of this is to avoid the unnecessary creation of an excessive number of individual vendor master records. For this reason, you may not store any vendor-specific data in a "one-time vendor" master record.

When creating a "one-time vendor" master record, you must assign a one-time account group. This account group determines that the vendor-specific fields are suppressed. You don’t need to enter this data until the time a purchasing document (e.g. a PO) is created.

When you create a purchasing document with a one-time vendor, you will be asked to enter the vendor address. Enter the vendor’s name and address.

Like all other master records, you can display, block, or delete one-time vendor master records.

static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned int lcr_h, old_cr; unsigned long flags; unsigned int baud, quot, clkdiv; unsigned int bits; if (uap->vendor->oversampling) clkdiv = 8; else clkdiv = 16; /* * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); #ifdef CONFIG_DMA_ENGINE /* * Adjust RX DMA polling rate with baud rate if not specified. */ if (uap->dmarx.auto_poll_rate) uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); #endif if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); else quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); switch (termios->c_cflag & CSIZE) { case CS5: lcr_h = UART01x_LCRH_WLEN_5; break; case CS6: lcr_h = UART01x_LCRH_WLEN_6; break; case CS7: lcr_h = UART01x_LCRH_WLEN_7; break; default: // CS8 lcr_h = UART01x_LCRH_WLEN_8; break; } if (termios->c_cflag & CSTOPB) lcr_h |= UART01x_LCRH_STP2; if (termios->c_cflag & PARENB) { lcr_h |= UART01x_LCRH_PEN; if (!(termios->c_cflag & PARODD)) lcr_h |= UART01x_LCRH_EPS; if (termios->c_cflag & CMSPAR) lcr_h |= UART011_LCRH_SPS; } if (uap->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; bits = tty_get_frame_size(termios->c_cflag); spin_lock_irqsave(&port->lock, flags); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baud); /* * Calculate the approximated time it takes to transmit one character * with the given baud rate. We use this as the poll interval when we * wait for the tx queue to empty. */ uap->rs485_tx_drain_interval = DIV_ROUND_UP(bits * 1000 * 1000, baud); pl011_setup_status_masks(port, termios); if (UART_ENABLE_MS(port, termios->c_cflag)) pl011_enable_ms(port); if (port->rs485.flags & SER_RS485_ENABLED) termios->c_cflag &= ~CRTSCTS; old_cr = pl011_read(uap, REG_CR); if (termios->c_cflag & CRTSCTS) { if (old_cr & UART011_CR_RTS) old_cr |= UART011_CR_RTSEN; old_cr |= UART011_CR_CTSEN; port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; } else { old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN); port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS); } if (uap->vendor->oversampling) { if (baud > port->uartclk / 16) old_cr |= ST_UART011_CR_OVSFACT; else old_cr &= ~ST_UART011_CR_OVSFACT; } /* * Workaround for the ST Micro oversampling variants to * increase the bitrate slightly, by lowering the divisor, * to avoid delayed sampling of start bit at high speeds, * else we see data corruption. */ if (uap->vendor->oversampling) { if ((baud >= 3000000) && (baud < 3250000) && (quot > 1)) quot -= 1; else if ((baud > 3250000) && (quot > 2)) quot -= 2; } /* Set baud rate */ pl011_write(quot & 0x3f, uap, REG_FBRD); pl011_write(quot >> 6, uap, REG_IBRD); /* * ----------v----------v----------v----------v----- * NOTE: REG_LCRH_TX and REG_LCRH_RX MUST BE WRITTEN AFTER * REG_FBRD & REG_IBRD. * ----------^----------^----------^----------^----- */ pl011_write_lcr_h(uap, lcr_h); pl011_write(old_cr, uap, REG_CR); spin_unlock_irqrestore(&port->lock, flags); }
最新发布
03-14
<think>好的,我现在需要分析用户提供的这段`static void pl011_set_termios`函数代码。首先,用户希望了解这个函数的功能和实现细节。我应该从代码的结构、关键变量和主要逻辑入手,逐步拆解。 首先,这个函数属于Linux内核中的UART驱动,特别是针对PL011类型的UART控制器。函数名中的`set_termios`提示它与终端I/O设置有关,通常用于配置串口参数,比如波特率、数据位、停止位等。参数包括`uart_port`、`termios`和`old`,其中`termios`保存新的配置,`old`可能是旧的配置,用于比较或恢复。 接下来,函数内部首先通过`container_of`宏获取`uart_amba_port`结构体指针`uap`,这应该是将通用的`uart_port`转换为具体的驱动数据结构。然后声明了一些变量,比如`lcr_h`用于配置行控制寄存器,`old_cr`保存原来的控制寄存器值,`flags`用于中断保护,还有波特率相关的`baud`、`quot`、`clkdiv`,以及数据位数的`bits`。 接下来,代码根据`uap->vendor->oversampling`决定时钟分频系数`clkdiv`为8或16。这可能与不同的硬件版本有关,过采样模式可能需要不同的分频。 然后调用`uart_get_baud_rate`计算波特率,这里传入的最大波特率是`port->uartclk / clkdiv`,即时钟频率除以分频系数。接着在DMA引擎启用的情况下,调整接收DMA的轮询速率,与波特率相关联,确保数据传输的及时性。 计算波特率分频器`quot`的部分,根据波特率是否大于`port->uartclk/16`来决定使用8倍还是4倍的分频系数。这里可能涉及到不同的分频模式,比如当波特率较高时,需要更精细的分频。 然后是设置数据位数的部分,通过`termios->c_cflag & CSIZE`来判断数据位是5、6、7还是8位,对应设置`lcr_h`的相应位。接着处理停止位,如果`CSTOPB`标志被设置,则使用两位停止位,否则一位。 校验位的设置部分,如果启用校验(`PARENB`),则设置`PEN`位,并根据是否为奇校验(`PARODD`)来设置`EPS`位(偶校验)。如果使用强制校验(`CMSPAR`),则设置`SPS`位。这些步骤都是在构建行控制寄存器`lcr_h`的值。 如果FIFO大小大于1,则启用FIFO(`FEN`位)。然后通过`tty_get_frame_size`计算每帧的位数,用于后续的超时和DMA轮询间隔的计算。 进入自旋锁保护区域,防止并发访问。更新端口的超时时间,确保数据传输在合理的时间内完成。计算发送排空间隔`rs485_tx_drain_interval`,用于RS485模式下的定时。 调用`pl011_setup_status_masks`设置状态掩码,可能涉及中断使能。如果启用了调制解调器状态(`CRTSCTS`),则调用`pl011_enable_ms`启用相关功能。如果RS485启用,则清除`CRTSCTS`标志,因为两者可能冲突。 处理硬件流控制(CRTSCTS),根据是否启用设置相应的控制寄存器位,并更新端口状态。对于过采样的特定处理,调整`old_cr`中的过采样因子位。 接下来是波特率分频器的写入,分为小数部分`FBRD`和整数部分`IBRD`。特别需要注意的是,注释指出必须先写这两个寄存器,再设置`LCRH`,这可能是因为某些硬件的配置顺序要求,避免波特率配置错误。 最后,恢复中断状态并释放锁。整个流程确保了串口参数的原子性更新,避免在配置过程中被中断或其他进程干扰。 需要验证的关键点包括:波特率计算是否正确,寄存器写入顺序是否必要,以及不同硬件变体(如ST微的过采样)的处理是否正确。此外,DMA和RS485相关的代码是否在相应配置启用时才执行,比如`#ifdef CONFIG_DMA_ENGINE`和`port->rs485.flags`的条件判断。 可能的问题点:在调整`quot`时的条件判断是否覆盖了所有情况,特别是波特率非常高时的处理;过采样模式下的分频调整是否会导致波特率误差;以及锁的保护范围是否足够,确保所有寄存器操作在锁内完成。 总结来说,这个函数负责根据新的termios配置,调整PL011 UART的波特率、数据位、停止位、校验位、流控制等参数,并进行必要的硬件寄存器配置,同时处理DMA和RS485等特定功能。</think>以下是对`pl011_set_termios`函数的逐层解析: --- ### **功能概述** 这是Linux内核中PL011 UART驱动程序的终端属性设置函数,负责根据`termios`结构体配置串口的波特率、数据位、校验位、停止位等参数,并更新硬件寄存器。 --- ### **代码结构分析** #### **1. 变量初始化** ```c struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); ``` - 通过`container_of`宏将通用`uart_port`转换为具体的`uart_amba_port`结构体(PL011硬件抽象)。 #### **2. 时钟分频选择** ```c if (uap->vendor->oversampling) clkdiv = 8; else clkdiv = 16; ``` - 根据硬件是否支持过采样(如ST Micro变体),设置时钟分频系数为8或16。 #### **3. 波特率计算** ```c baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / clkdiv); ``` - 调用核心函数计算目标波特率,最大允许值为`时钟频率 / clkdiv`。 #### **4. DMA轮询速率调整(可选)** ```c #ifdef CONFIG_DMA_ENGINE if (uap->dmarx.auto_poll_rate) uap->dmarx.poll_rate = DIV_ROUND_UP(10000000, baud); #endif ``` - 若启用DMA且配置为自动轮询,则根据波特率调整接收DMA的轮询间隔。 #### **5. 波特率分频器计算** ```c if (baud > port->uartclk/16) quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); else quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); ``` - 分频器`quot`的计算取决于波特率是否超过阈值,选择8倍或4倍分频模式。 #### **6. 数据位与停止位配置** ```c switch (termios->c_cflag & CSIZE) { case CS5: lcr_h = UART01x_LCRH_WLEN_5; break; // ...其他数据位配置 } if (termios->c_cflag & CSTOPB) lcr_h |= UART01x_LCRH_STP2; ``` - 根据`CSIZE`设置数据位长度(5-8位),`CSTOPB`标志决定使用1或2位停止位。 #### **7. 校验位设置** ```c if (termios->c_cflag & PARENB) { lcr_h |= UART01x_LCRH_PEN; if (!(termios->c_cflag & PARODD)) lcr_h |= UART01x_LCRH_EPS; if (termios->c_cflag & CMSPAR) lcr_h |= UART011_LCRH_SPS; } ``` - 启用校验(`PARENB`),配置奇偶校验类型(`PARODD`为奇校验),`CMSPAR`表示强制校验位。 #### **8. FIFO启用** ```c if (uap->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; ``` - 若硬件支持FIFO且深度大于1,则启用FIFO缓冲。 #### **9. 临界区保护** ```c spin_lock_irqsave(&port->lock, flags); ``` - 加锁防止并发访问,确保寄存器操作的原子性。 #### **10. 超时与RS485定时** ```c uart_update_timeout(port, termios->c_cflag, baud); uap->rs485_tx_drain_interval = DIV_ROUND_UP(bits * 1000 * 1000, baud); ``` - 更新串口超时时间,计算RS485模式下发送排空间隔(基于每字符传输时间)。 #### **11. 硬件流控制** ```c if (termios->c_cflag & CRTSCTS) { old_cr |= UART011_CR_CTSEN | UART011_CR_RTSEN; port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; } ``` - 若启用RTS/CTS流控,设置控制寄存器并更新端口状态。 #### **12. 过采样特殊处理** ```c if (uap->vendor->oversampling) { if (baud > port->uartclk / 16) old_cr |= ST_UART011_CR_OVSFACT; // ...波特率补偿逻辑 } ``` - 针对过采样硬件的波特率补偿,防止高速下的数据损坏。 #### **13. 寄存器写入顺序** ```c pl011_write(quot & 0x3f, uap, REG_FBRD); // 小数部分 pl011_write(quot >> 6, uap, REG_IBRD); // 整数部分 pl011_write_lcr_h(uap, lcr_h); // 行控制寄存器 ``` - **必须严格按照顺序**:先写波特率分频寄存器,再配置行控制寄存器(`LCRH`)。 --- ### **关键点总结** 1. **原子性操作**:通过自旋锁保护寄存器配置过程。 2. **硬件适配**:处理不同厂商(如ST Micro)的过采样特性。 3. **波特率精度**:分频器计算与补偿逻辑确保波特率误差最小。 4. **功能冲突处理**:RS485启用时自动禁用硬件流控(`CRTSCTS`)。 --- ### **典型应用场景** 此函数在以下情况被调用: - 用户通过`tcsetattr`设置串口参数时。 - 内核初始化或重新配置UART设备时。 通过逐层配置寄存器,确保串口按指定参数可靠工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值