uart操作

打开串口:tty_open

  1971  /**
  1972   *      tty_open                -       open a tty device
  1973   *      @inode: inode of device file
  1974   *      @filp: file pointer to tty
  1975   *
  1976   *      tty_open and tty_release keep up the tty count that contains the
  1977   *      number of opens done on a tty. We cannot use the inode-count, as
  1978   *      different inodes might point to the same tty.
  1979   *
  1980   *      Open-counting is needed for pty masters, as well as for keeping
  1981   *      track of serial lines: DTR is dropped when the last close happens.
  1982   *      (This is not done solely through tty->count, now.  - Ted 1/27/92)
  1983   *
  1984   *      The termios state of a pty is reset on first open so that
  1985   *      settings don't persist across reuse.
  1986   *
  1987   *      Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
  1988   *               tty->count should protect the rest.
  1989   *               ->siglock protects ->signal/->sighand
  1990   *
  1991   *      Note: the tty_unlock/lock cases without a ref are only safe due to
  1992   *      tty_mutex
  1993   */
  1994
  1995  static int tty_open(struct inode *inode, struct file *filp)
  1996  {
  1997          struct tty_struct *tty;
  1998          int noctty, retval;
  1999          dev_t device = inode->i_rdev;
  2000          unsigned saved_flags = filp->f_flags;
  2001
  2002          nonseekable_open(inode, filp);
  2003
  2004  retry_open:
  2005          retval = tty_alloc_file(filp);
  2006          if (retval)
  2007                  return -ENOMEM;
  2008
  2009          tty = tty_open_current_tty(device, filp);
  2010          if (!tty)
  2011                  tty = tty_open_by_driver(device, inode, filp);
  2012
  2013          if (IS_ERR(tty)) {
  2014                  tty_free_file(filp);
  2015                  retval = PTR_ERR(tty);
  2016                  if (retval != -EAGAIN || signal_pending(current))
  2017                          return retval;
  2018                  schedule();
  2019                  goto retry_open;
  2020          }
  2021
  2022          tty_add_file(tty, filp);
  2023
  2024          check_tty_count(tty, __func__);
  2025          tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
  2026
  2027          if (tty->ops->open)
  2028                  retval = tty->ops->open(tty, filp);
  2029          else
  2030                  retval = -ENODEV;
  2031          filp->f_flags = saved_flags;
  2032
  2033          if (retval) {
  2034                  tty_debug_hangup(tty, "open error %d, releasing\n", retval);
  2035
  2036                  tty_unlock(tty); /* need to call tty_release without BTM */
  2037                  tty_release(inode, filp);
  2038                  if (retval != -ERESTARTSYS)
  2039                          return retval;
  2040
  2041                  if (signal_pending(current))
  2042                          return retval;
  2043
  2044                  schedule();
  2045                  /*
  2046                   * Need to reset f_op in case a hangup happened.
  2047                   */
  2048                  if (tty_hung_up_p(filp))
  2049                          filp->f_op = &tty_fops;
  2050                  goto retry_open;
  2051          }
  2052          clear_bit(TTY_HUPPED, &tty->flags);
  2053
  2054          noctty = (filp->f_flags & O_NOCTTY) ||
  2055                   (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
  2056                   device == MKDEV(TTYAUX_MAJOR, 1) ||
  2057                   (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
  2058                    tty->driver->subtype == PTY_TYPE_MASTER);
  2059          if (!noctty)
  2060                  tty_open_proc_set_tty(filp, tty);
  2061          tty_unlock(tty);
  2062          return 0;
  2063  }
  2064
  

第2002行:nonseekable_open(inode, filp);

/*
 * This is used by subsystems that don't want seekable
 * file descriptors. The function is not supposed to ever fail, the only
 * reason it returns an 'int' and not 'void' is so that it can be plugged
 * directly into file_operations structure.
 */
int nonseekable_open(struct inode *inode, struct file *filp)
{
    filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
    return 0;
}

EXPORT_SYMBOL(nonseekable_open);

2005行 retval = tty_alloc_file(filp);

 182  int tty_alloc_file(struct file *file)
   183  {
   184          struct tty_file_private *priv;
   185
   186          priv = kmalloc(sizeof(*priv), GFP_KERNEL);
   187          if (!priv)
   188                  return -ENOMEM;
   189
   190          file->private_data = priv;
   191
   192          return 0;
   193  }

分配一个tty_file_private结构体并赋值给文件的private_data成员

2009行: tty = tty_open_current_tty(device, filp);

  1770  /**
  1771   *      tty_open_current_tty - get locked tty of current task
  1772   *      @device: device number
  1773   *      @filp: file pointer to tty
  1774   *      @return: locked tty of the current task iff @device is /dev/tty
  1775   *
  1776   *      Performs a re-open of the current task's controlling tty.
  1777   *
  1778   *      We cannot return driver and index like for the other nodes because
  1779   *      devpts will not work then. It expects inodes to be from devpts FS.
  1780   */
  1781  static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
  1782  {
  1783          struct tty_struct *tty;
  1784          int retval;
  1785
  1786          if (device != MKDEV(TTYAUX_MAJOR, 0))
  1787                  return NULL;
  1788
  1789          tty = get_current_tty();
  1790          if (!tty)
  1791                  return ERR_PTR(-ENXIO);
  1792
  1793          filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
  1794          /* noctty = 1; */
  1795          tty_lock(tty);
  1796          tty_kref_put(tty);      /* safe to drop the kref now */
  1797
  1798          retval = tty_reopen(tty);
  1799          if (retval < 0) {
  1800                  tty_unlock(tty);
  1801                  tty = ERR_PTR(retval);
  1802          }
  1803          return tty;
  1804  }

第1786~1787行:看到咱们返回NULL;

退出这个函数;运行 tty = tty_open_by_driver(device, inode, filp);

 1675  /**
  1676   *      tty_open_by_driver      -       open a tty device
  1677   *      @device: dev_t of device to open
  1678   *      @inode: inode of device file
  1679   *      @filp: file pointer to tty
  1680   *
  1681   *      Performs the driver lookup, checks for a reopen, or otherwise
  1682   *      performs the first-time tty initialization.
  1683   *
  1684   *      Returns the locked initialized or re-opened &tty_struct
  1685   *
  1686   *      Claims the global tty_mutex to serialize:
  1687   *        - concurrent first-time tty initialization
  1688   *        - concurrent tty driver removal w/ lookup
  1689   *        - concurrent tty removal from driver table
  1690   */
  1691  static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
  1692                                               struct file *filp)
  1693  {
  1694          struct tty_struct *tty;
  1695          struct tty_driver *driver = NULL;
  1696          int index = -1;
  1697          int retval;

  1698          mutex_lock(&tty_mutex);
  1699          driver = tty_lookup_driver(device, filp, &index);
  1700          if (IS_ERR(driver)) {
  1701                  mutex_unlock(&tty_mutex);
  1702                  return ERR_CAST(driver);
  1703          }

  1704          /* check whether we're reopening an existing tty */
  1705          tty = tty_driver_lookup_tty(driver, filp, index);
  1706          if (IS_ERR(tty)) {
  1707                  mutex_unlock(&tty_mutex);
  1708                  goto out;
  1709          }

  1710          if (tty) {
  1711                  if (tty_port_kopened(tty->port)) {
  1712                          tty_kref_put(tty);
  1713                          mutex_unlock(&tty_mutex);
  1714                          tty = ERR_PTR(-EBUSY);
  1715                          goto out;
  1716                  }
  1717                  mutex_unlock(&tty_mutex);
  1718                  retval = tty_lock_interruptible(tty);
  1719                  tty_kref_put(tty);  /* drop kref from tty_driver_lookup_tty() */
  1720                  if (retval) {
  1721                          if (retval == -EINTR)
  1722                                  retval = -ERESTARTSYS;
  1723                          tty = ERR_PTR(retval);
  1724                          goto out;
  1725                  }
  1726                  retval = tty_reopen(tty);
  1727                  if (retval < 0) {
  1728                          tty_unlock(tty);
  1729                          tty = ERR_PTR(retval);
  1730                  }
  1731          } else { /* Returns with the tty_lock held for now */
  1732                  tty = tty_init_dev(driver, index);
  1733                  mutex_unlock(&tty_mutex);
  1734          }
  1735  out:
  1736          tty_driver_kref_put(driver);
  1737          return tty;
  1738  }

第 1699 行 driver = tty_lookup_driver(device, filp, &index); /* 根据设备号找到对应的tty_driver */

第 1705 tty = tty_driver_lookup_tty(driver, filp, index);// /* 检查我们是否重复打开tty */

第1710 判断如果重复打开则执行该分支

第1732行: tty = tty_init_dev(driver, index);

 1123  /**
  1124   *      tty_init_dev            -       initialise a tty device
  1125   *      @driver: tty driver we are opening a device on
  1126   *      @idx: device index
  1127   *      @ret_tty: returned tty structure
  1128   *
  1129   *      Prepare a tty device. This may not be a "new" clean device but
  1130   *      could also be an active device. The pty drivers require special
  1131   *      handling because of this.
  1132   *
  1133   *      Locking:
  1134   *              The function is called under the tty_mutex, which
  1135   *      protects us from the tty struct or driver itself going away.
  1136   *
  1137   *      On exit the tty device has the line discipline attached and
  1138   *      a reference count of 1. If a pair was created for pty/tty use
  1139   *      and the other was a pty master then it too has a reference count of 1.
  1140   *
  1141   * WSH 06/09/97: Rewritten to remove races and properly clean up after a
  1142   * failed open.  The new code protects the open with a mutex, so it's
  1143   * really quite straightforward.  The mutex locking can probably be
  1144   * relaxed for the (most common) case of reopening a tty.
  1145   */

  1146  struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
  1147  {
  1148          struct tty_struct *tty;
  1149          int retval;

  1150          /*
  1151           * First time open is complex, especially for PTY devices.
  1152           * This code guarantees that either everything succeeds and the
  1153           * TTY is ready for operation, or else the table slots are vacated
  1154           * and the allocated memory released.  (Except that the termios
  1155           * may be retained.)
  1156           */

  1157          if (!try_module_get(driver->owner))
  1158                  return ERR_PTR(-ENODEV);

  1159          tty = alloc_tty_struct(driver, idx);
  1160          if (!tty) {
  1161                  retval = -ENOMEM;
  1162                  goto err_module_put;
  1163          }

  1164          tty_lock(tty);
  1165          retval = tty_driver_install_tty(driver, tty);
  1166          if (retval < 0)
  1167                  goto err_free_tty;

  1168          if (!tty->port)
  1169                  tty->port = driver->ports[idx];

  1170          WARN_RATELIMIT(!tty->port,
  1171                          "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
  1172                          __func__, tty->driver->name);

  1173          retval = tty_ldisc_lock(tty, 5 * HZ);
  1174          if (retval)
  1175                  goto err_release_lock;
  1176          tty->port->itty = tty;

  1177          /*
  1178           * Structures all installed ... call the ldisc open routines.
  1179           * If we fail here just call release_tty to clean up.  No need
  1180           * to decrement the use counts, as release_tty doesn't care.
  1181           */
  1182          retval = tty_ldisc_setup(tty, tty->link);
  1183          if (retval)
  1184                  goto err_release_tty;
  1185          tty_ldisc_unlock(tty);
  1186          /* Return the tty locked so that it cannot vanish under the caller */
  1187          return tty;

  1188  err_free_tty:
  1189          tty_unlock(tty);
  1190          free_tty_struct(tty);
  1191  err_module_put:
  1192          module_put(driver->owner);
  1193          return ERR_PTR(retval);

  1194          /* call the tty release_tty routine to clean out this slot */
  1195  err_release_tty:
  1196          tty_ldisc_unlock(tty);
  1197          tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
  1198                               retval, idx);
  1199  err_release_lock:
  1200          tty_unlock(tty);
  1201          release_tty(tty, idx);
  1202          return ERR_PTR(retval);
  1203  }

第1159~1163行: tty = alloc_tty_struct(driver, idx);分配一个

 2475  /**
  2476   *      alloc_tty_struct
  2477   *
  2478   *      This subroutine allocates and initializes a tty structure.
  2479   *
  2480   *      Locking: none - tty in question is not exposed at this point
  2481   */

  2482  struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
  2483  {
  2484          struct tty_struct *tty;

  2485          tty = kzalloc(sizeof(*tty), GFP_KERNEL);
  2486          if (!tty)
  2487                  return NULL;

  2488          kref_init(&tty->kref);
  2489          tty->magic = TTY_MAGIC;
  2490          if (tty_ldisc_init(tty)) {
  2491                  kfree(tty);
  2492                  return NULL;
  2493          }
  2494          tty->session = NULL;
  2495          tty->pgrp = NULL;
  2496          mutex_init(&tty->legacy_mutex);
  2497          mutex_init(&tty->throttle_mutex);
  2498          init_rwsem(&tty->termios_rwsem);
  2499          mutex_init(&tty->winsize_mutex);
  2500          init_ldsem(&tty->ldisc_sem);
  2501          init_waitqueue_head(&tty->write_wait);
  2502          init_waitqueue_head(&tty->read_wait);
  2503          INIT_WORK(&tty->hangup_work, do_tty_hangup);
  2504          mutex_init(&tty->atomic_write_lock);
  2505          spin_lock_init(&tty->ctrl_lock);
  2506          spin_lock_init(&tty->flow_lock);
  2507          spin_lock_init(&tty->files_lock);
  2508          INIT_LIST_HEAD(&tty->tty_files);
  2509          INIT_WORK(&tty->SAK_work, do_SAK_work);

  2510          tty->driver = driver;
  2511          tty->ops = driver->ops;
  2512          tty->index = idx;
  2513          tty_line_name(driver, idx, tty->name);
  2514          tty->dev = tty_get_device(tty);

  2515          return tty;
  2516  }

第2485~2487分配一个tty_struct内存。

第2488~2493行:tty_ldisc_init(tty)// /* 初始化线路规程 */

接下来初始化。退出。

第1165 行 retval = tty_driver_install_tty(driver, tty);// /* 该函数主要讲tty_struct的地址链接到tty_driver的成员中 */

1182 retval = tty_ldisc_setup(tty, tty->link); // /* 调用线程规程并打开 */

退出tty_init_dev。

第 1784 tty_add_file(tty, filp);

 174  /* Associate a new file with the tty structure */
   175  void tty_add_file(struct tty_struct *tty, struct file *file)
   176  {
   177          struct tty_file_private *priv = file->private_data;

   178          priv->tty = tty;
   179          priv->file = file;

   180          spin_lock(&tty->files_lock);
   181          list_add(&priv->list, &tty->tty_files);
   182          spin_unlock(&tty->files_lock);
   183  }

将当前文件的私有数据即tty_file_private成员进行填充,再将其链入tty_stuct的tty_files链表

第1787~1791行 retval = tty->ops->open(tty, filp); 运行这个函数是uart_open

 1460  /*
  1461   * Calls to uart_open are serialised by the tty_lock in
  1462   *   drivers/tty/tty_io.c:tty_open()
  1463   * Note that if this fails, then uart_close() _will_ be called.
  1464   *
  1465   * In time, we want to scrap the "opening nonpresent ports"
  1466   * behaviour and implement an alternative way for setserial
  1467   * to set base addresses/ports/types.  This will allow us to
  1468   * get rid of a certain amount of extra tests.
  1469   */
  1470  static int uart_open(struct tty_struct *tty, struct file *filp)
  1471  {
  1472          struct uart_driver *drv = tty->driver->driver_state;
  1473          int retval, line = tty->index;
  1474          struct uart_state *state = drv->state + line;

  1475          tty->driver_data = state;

  1476          retval = tty_port_open(&state->port, tty, filp);
  1477          if (retval > 0)
  1478                  retval = 0;

  1479          return retval;
  1480  }

第1475行: /* 设置tty_struct的driver_data成员为uart_state */

第1476行: retval = tty_port_open(&state->port, tty, filp); // /* 打开tty_port */

  591  /**
   592   * tty_port_open
   593   *
   594   * Caller holds tty lock.
   595   *
   596   * NB: may drop and reacquire tty lock (in tty_port_block_til_ready()) so
   597   * tty and tty_port may have changed state (eg., may be hung up now)
   598   */
   599  int tty_port_open(struct tty_port *port, struct tty_struct *tty,
   600                                                          struct file *filp)
   601  {
   602          spin_lock_irq(&port->lock);
   603          ++port->count;
   604          spin_unlock_irq(&port->lock);
   605          tty_port_tty_set(port, tty);

   606          /*
   607           * Do the device-specific open only if the hardware isn't
   608           * already initialized. Serialize open and shutdown using the
   609           * port mutex.
   610           */

   611          mutex_lock(&port->mutex);

   612          if (!tty_port_initialized(port)) {
   613                  clear_bit(TTY_IO_ERROR, &tty->flags);
   614                  if (port->ops->activate) {
   615                          int retval = port->ops->activate(port, tty);
   616                          if (retval) {
   617                                  mutex_unlock(&port->mutex);
   618                                  return retval;
   619                          }
   620                  }
   621                  tty_port_set_initialized(port, 1);
   622          }
   623          mutex_unlock(&port->mutex);
   624          return tty_port_block_til_ready(port, tty, filp);
   625  }

第612行:tty_port_initialized(port)检测tty_port是否初始化过

static inline bool tty_port_initialized(struct tty_port *port)
{
    return test_bit(TTY_PORT_INITIALIZED, &port->iflags);
}

第614~619行:int retval = port->ops->activate(port, tty);

 1481  static int uart_port_activate(struct tty_port *port, struct tty_struct *tty)
  1482  {
  1483          struct uart_state *state = container_of(port, struct uart_state, port);
  1484          struct uart_port *uport;

  1485          uport = uart_port_check(state);
  1486          if (!uport || uport->flags & UPF_DEAD)
  1487                  return -ENXIO;

  1488          port->low_latency = (uport->flags & UPF_LOW_LATENCY) ? 1 : 0;

  1489          /*
  1490           * Start up the serial port.
  1491           */
  1492          return uart_startup(tty, state, 0);
  1493  }
    

第1492行:uart_startup(tty, state, 0);

 221  static int uart_startup(struct tty_struct *tty, struct uart_state *state,
   222                  int init_hw)
   223  {
   224          struct tty_port *port = &state->port;
   225          int retval;

   226          if (tty_port_initialized(port))
   227                  return 0;

   228          retval = uart_port_startup(tty, state, init_hw);
   229          if (retval)
   230                  set_bit(TTY_IO_ERROR, &tty->flags);

   231          return retval;
   232  }

第228行: retval = uart_port_startup(tty, state, init_hw);

163  /*
   164   * Startup the port.  This will be called once per open.  All calls
   165   * will be serialised by the per-port mutex.
   166   */
   167  static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
   168                  int init_hw)
   169  {
   170          struct uart_port *uport = uart_port_check(state);
   171          unsigned long page;
   172          unsigned long flags = 0;
   173          int retval = 0;

   174          if (uport->type == PORT_UNKNOWN)
   175                  return 1;

   176          /*
   177           * Make sure the device is in D0 state.
   178           */
   179          uart_change_pm(state, UART_PM_STATE_ON);

   180          /*
   181           * Initialise and allocate the transmit and temporary
   182           * buffer.
   183           */
   184          page = get_zeroed_page(GFP_KERNEL);
   185          if (!page)
   186                  return -ENOMEM;

   187          uart_port_lock(state, flags);
   188          if (!state->xmit.buf) {
   189                  state->xmit.buf = (unsigned char *) page;
   190                  uart_circ_clear(&state->xmit);
   191          } else {
   192                  free_page(page);
   193          }
   194          uart_port_unlock(uport, flags);

   195          retval = uport->ops->startup(uport);
   196          if (retval == 0) {
   197                  if (uart_console(uport) && uport->cons->cflag) {
   198                          tty->termios.c_cflag = uport->cons->cflag;
   199                          uport->cons->cflag = 0;
   200                  }
   201                  /*
   202                   * Initialise the hardware port settings.
   203                   */
   204                  uart_change_speed(tty, state, NULL);

   205                  /*
   206                   * Setup the RTS and DTR signals once the
   207                   * port is open and ready to respond.
   208                   */
   209                  if (init_hw && C_BAUD(tty))
   210                          uart_port_dtr_rts(uport, 1);
   211          }

     212          /*
   213           * This is to allow setserial on this port. People may want to set
   214           * port/irq/type and then reconfigure the port properly if it failed
   215           * now.
   216           */
   217          if (retval && capable(CAP_SYS_ADMIN))
   218                  return 1;

   219          return retval;
   220  }

第187~194行:

第188行,判断是否有缓存。如果没有分配内存。

第195~211行

195 retval = uport->ops->startup(uport);* Setup the RTS and DTR signals once the

这个函数对应serial8250_startup

  2103  static int serial8250_startup(struct uart_port *port)
  2104  {
  2105          if (port->startup)
  2106                  return port->startup(port);
  2107          return serial8250_do_startup(port);
  2108  }

第2106行运行omap_8250_startup

       514  static int omap_8250_startup(struct uart_port *port)
   515  {
   516          struct uart_8250_port *up = up_to_u8250p(port);
   517          struct omap8250_priv *priv = port->private_data;
   518          int ret;

   519          if (priv->wakeirq) {
   520                  ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq);
   521                  if (ret)
   522                          return ret;
   523          }
   524          printk("omap_8250_startup\r\n");
   525          pm_runtime_get_sync(port->dev);

   526          up->mcr = 0;
   527          serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);

   528          serial_out(up, UART_LCR, UART_LCR_WLEN8);

   529          up->lsr_saved_flags = 0;
   530          up->msr_saved_flags = 0;

   531          /* Disable DMA for console UART */
   532          if (uart_console(port))
   533                  up->dma = NULL;

   534          if (up->dma) {
   535                  ret = serial8250_request_dma(up);
   536                  if (ret) {
   537                          dev_warn_ratelimited(port->dev,
   538                                               "failed to request DMA\n");
   539                          up->dma = NULL;
   540                  }
   541          }

   542          ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED,
   543                            dev_name(port->dev), port);
   544          if (ret < 0)
   545                  goto err;

   546          up->ier = UART_IER_RLSI | UART_IER_RDI;
   547          serial_out(up, UART_IER, up->ier);

   548  #ifdef CONFIG_PM
   549          up->capabilities |= UART_CAP_RPM;
   550  #endif

   551          /* Enable module level wake up */
   552          priv->wer = OMAP_UART_WER_MOD_WKUP;
   553          if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP)
   554                  priv->wer |= OMAP_UART_TX_WAKEUP_EN;
   555          serial_out(up, UART_OMAP_WER, priv->wer);

   556          if (up->dma && !(priv->habit & UART_HAS_EFR2))
   557                  up->dma->rx_dma(up);

   558          pm_runtime_mark_last_busy(port->dev);
   559          pm_runtime_put_autosuspend(port->dev);
   560          return 0;
   561  err:
   562          pm_runtime_mark_last_busy(port->dev);
   563          pm_runtime_put_autosuspend(port->dev);
   564          dev_pm_clear_wake_irq(port->dev);
   565          return ret;
   566  }

第526行:up->mcr = 0;

第527行;serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);

清除FIFO的读写缓存。

第528 serial_out(up, UART_LCR, UART_LCR_WLEN8);

传输为8位。

第 529 行 up->lsr_saved_flags = 0;

第 530 行 up->msr_saved_flags = 0;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值