📄 ps3-vuart.c
字号:
* Used to clear pending rx interrupt source. Will not block. */void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, unsigned int bytes){ int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); u64 bytes_waiting; void* tmp; result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting); BUG_ON(result); bytes = bytes ? min(bytes, (unsigned int)bytes_waiting) : bytes_waiting; dev_dbg(&dev->core, "%s:%d: %u\n", __func__, __LINE__, bytes); if (!bytes) return; /* Add some extra space for recently arrived data. */ bytes += 128; tmp = kmalloc(bytes, GFP_KERNEL); if (!tmp) return; ps3_vuart_raw_read(dev, tmp, bytes, &bytes_waiting); kfree(tmp); /* Don't include these bytes in the stats. */ priv->stats.bytes_read -= bytes_waiting;}EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes);/** * struct list_buffer - An element for a port device fifo buffer list. */struct list_buffer { struct list_head link; const unsigned char *head; const unsigned char *tail; unsigned long dbg_number; unsigned char data[];};/** * ps3_vuart_write - the entry point for writing data to a port * @dev: The struct ps3_system_bus_device instance. * * If the port is idle on entry as much of the incoming data is written to * the port as the port will accept. Otherwise a list buffer is created * and any remaning incoming data is copied to that buffer. The buffer is * then enqueued for transmision via the transmit interrupt. */int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, unsigned int bytes){ static unsigned long dbg_number; int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb; dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); spin_lock_irqsave(&priv->tx_list.lock, flags); if (list_empty(&priv->tx_list.head)) { unsigned long bytes_written; result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); spin_unlock_irqrestore(&priv->tx_list.lock, flags); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_vuart_raw_write failed\n", __func__, __LINE__); return result; } if (bytes_written == bytes) { dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n", __func__, __LINE__, bytes); return 0; } bytes -= bytes_written; buf += bytes_written; } else spin_unlock_irqrestore(&priv->tx_list.lock, flags); lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); if (!lb) { return -ENOMEM; } memcpy(lb->data, buf, bytes); lb->head = lb->data; lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; spin_lock_irqsave(&priv->tx_list.lock, flags); list_add_tail(&lb->link, &priv->tx_list.head); ps3_vuart_enable_interrupt_tx(dev); spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); return 0;}EXPORT_SYMBOL_GPL(ps3_vuart_write);/** * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list. * @dev: The struct ps3_system_bus_device instance. * @bytes_queued: Number of bytes queued to the buffer list. * * Must be called with priv->rx_list.lock held. */static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev, u64 *bytes_queued){ static unsigned long dbg_number; int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); struct list_buffer *lb; u64 bytes; *bytes_queued = 0; result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); BUG_ON(result); if (result) return -EIO; if (!bytes) return 0; /* Add some extra space for recently arrived data. */ bytes += 128; lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); if (!lb) return -ENOMEM; ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); lb->head = lb->data; lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; list_add_tail(&lb->link, &priv->rx_list.head); priv->rx_list.bytes_held += bytes; dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); *bytes_queued = bytes; return 0;}/** * ps3_vuart_read - The entry point for reading data from a port. * * Queue data waiting at the port, and if enough bytes to satisfy the request * are held in the buffer list those bytes are dequeued and copied to the * caller's buffer. Emptied list buffers are retiered. If the request cannot * be statified by bytes held in the list buffers -EAGAIN is returned. */int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, unsigned int bytes){ int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_read; dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); spin_lock_irqsave(&priv->rx_list.lock, flags); /* Queue rx bytes here for polled reads. */ while (priv->rx_list.bytes_held < bytes) { u64 tmp; result = ps3_vuart_queue_rx_bytes(dev, &tmp); if (result || !tmp) { dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", __func__, __LINE__, bytes - priv->rx_list.bytes_held); spin_unlock_irqrestore(&priv->rx_list.lock, flags); return -EAGAIN; } } list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) { bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); memcpy(buf, lb->head, bytes_read); buf += bytes_read; bytes -= bytes_read; priv->rx_list.bytes_held -= bytes_read; if (bytes_read < lb->tail - lb->head) { lb->head += bytes_read; dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " "bytes\n", __func__, __LINE__, lb->dbg_number, bytes_read); spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh " "bytes\n", __func__, __LINE__, lb->dbg_number, bytes_read); list_del(&lb->link); kfree(lb); } spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0;}EXPORT_SYMBOL_GPL(ps3_vuart_read);/** * ps3_vuart_work - Asynchronous read handler. */static void ps3_vuart_work(struct work_struct *work){ struct ps3_system_bus_device *dev = ps3_vuart_work_to_system_bus_dev(work); struct ps3_vuart_port_driver *drv = ps3_system_bus_dev_to_vuart_drv(dev); BUG_ON(!drv); drv->work(dev);}int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes){ struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; if (priv->rx_list.work.trigger) { dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", __func__, __LINE__); return -EAGAIN; } BUG_ON(!bytes); PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); spin_lock_irqsave(&priv->rx_list.lock, flags); if (priv->rx_list.bytes_held >= bytes) { dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", __func__, __LINE__, bytes); schedule_work(&priv->rx_list.work.work); spin_unlock_irqrestore(&priv->rx_list.lock, flags); return 0; } priv->rx_list.work.trigger = bytes; spin_unlock_irqrestore(&priv->rx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); return 0;}EXPORT_SYMBOL_GPL(ps3_vuart_read_async);void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev){ to_port_priv(dev)->rx_list.work.trigger = 0;}EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async);/** * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler * * Services the transmit interrupt for the port. Writes as much data from the * buffer list as the port will accept. Retires any emptied list buffers and * adjusts the final list buffer state for a partial write. */static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev){ int result = 0; struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; struct list_buffer *lb, *n; unsigned long bytes_total = 0; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); spin_lock_irqsave(&priv->tx_list.lock, flags); list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) { unsigned long bytes_written; result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head, &bytes_written); if (result) { dev_dbg(&dev->core, "%s:%d: ps3_vuart_raw_write failed\n", __func__, __LINE__); break; } bytes_total += bytes_written; if (bytes_written < lb->tail - lb->head) { lb->head += bytes_written; dev_dbg(&dev->core, "%s:%d cleared buf_%lu, %lxh bytes\n", __func__, __LINE__, lb->dbg_number, bytes_written); goto port_full; } dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, lb->dbg_number); list_del(&lb->link); kfree(lb); } ps3_vuart_disable_interrupt_tx(dev);port_full: spin_unlock_irqrestore(&priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", __func__, __LINE__, bytes_total); return result;}/** * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler * * Services the receive interrupt for the port. Creates a list buffer and * copies all waiting port data to that buffer and enqueues the buffer in the * buffer list. Buffer list data is dequeued via ps3_vuart_read. */static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev){ int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long flags; u64 bytes; dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); spin_lock_irqsave(&priv->rx_list.lock, flags); result = ps3_vuart_queue_rx_bytes(dev, &bytes); if (result) { spin_unlock_irqrestore(&priv->rx_list.lock, flags); return result; } if (priv->rx_list.work.trigger && priv->rx_list.bytes_held >= priv->rx_list.work.trigger) { dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", __func__, __LINE__, priv->rx_list.work.trigger); priv->rx_list.work.trigger = 0; schedule_work(&priv->rx_list.work.work); } spin_unlock_irqrestore(&priv->rx_list.lock, flags); return result;}static int ps3_vuart_handle_interrupt_disconnect( struct ps3_system_bus_device *dev){ dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); BUG_ON("no support"); return -1;}/** * ps3_vuart_handle_port_interrupt - second stage interrupt handler * * Services any pending interrupt types for the port. Passes control to the * third stage type specific interrupt handler. Returns control to the first * stage handler after one iteration. */static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev){ int result; struct ps3_vuart_port_priv *priv = to_port_priv(dev); unsigned long status; result = ps3_vuart_get_interrupt_status(dev, &status); if (result)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -