cregit-Linux how code gets into the kernel

Release 4.11 drivers/staging/greybus/uart.c

/*
 * UART driver for the Greybus "generic" UART module.
 *
 * Copyright 2014 Google Inc.
 * Copyright 2014 Linaro Ltd.
 *
 * Released under the GPLv2 only.
 *
 * Heavily based on drivers/usb/class/cdc-acm.c and
 * drivers/usb/serial/usb-serial.c.
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/sched/signal.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/idr.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/kfifo.h>
#include <linux/workqueue.h>
#include <linux/completion.h>

#include "greybus.h"
#include "gbphy.h"


#define GB_NUM_MINORS	16	
/* 16 is is more than enough */

#define GB_NAME		"ttyGB"


#define GB_UART_WRITE_FIFO_SIZE		PAGE_SIZE

#define GB_UART_WRITE_ROOM_MARGIN	1	
/* leave some space in fifo */

#define GB_UART_FIRMWARE_CREDITS	4096

#define GB_UART_CREDIT_WAIT_TIMEOUT_MSEC	10000


struct gb_tty_line_coding {
	
__le32	rate;
	
__u8	format;
	
__u8	parity;
	
__u8	data_bits;
	
__u8	flow_control;
};


struct gb_tty {
	
struct gbphy_device *gbphy_dev;
	
struct tty_port port;
	
void *buffer;
	
size_t buffer_payload_max;
	
struct gb_connection *connection;
	
u16 cport_id;
	
unsigned int minor;
	
unsigned char clocal;
	
bool disconnected;
	
spinlock_t read_lock;
	
spinlock_t write_lock;
	
struct async_icount iocount;
	
struct async_icount oldcount;
	
wait_queue_head_t wioctl;
	
struct mutex mutex;
	
u8 ctrlin;	/* input control lines */
	
u8 ctrlout;	/* output control lines */
	
struct gb_tty_line_coding line_coding;
	
struct work_struct tx_work;
	
struct kfifo write_fifo;
	
bool close_pending;
	
unsigned int credits;
	
struct completion credits_complete;
};


static struct tty_driver *gb_tty_driver;
static DEFINE_IDR(tty_minors);
static DEFINE_MUTEX(table_lock);


static int gb_uart_receive_data_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = gb_connection_get_data(connection); struct tty_port *port = &gb_tty->port; struct gb_message *request = op->request; struct gb_uart_recv_data_request *receive_data; u16 recv_data_size; int count; unsigned long tty_flags = TTY_NORMAL; if (request->payload_size < sizeof(*receive_data)) { dev_err(&gb_tty->gbphy_dev->dev, "short receive-data request received (%zu < %zu)\n", request->payload_size, sizeof(*receive_data)); return -EINVAL; } receive_data = op->request->payload; recv_data_size = le16_to_cpu(receive_data->size); if (recv_data_size != request->payload_size - sizeof(*receive_data)) { dev_err(&gb_tty->gbphy_dev->dev, "malformed receive-data request received (%u != %zu)\n", recv_data_size, request->payload_size - sizeof(*receive_data)); return -EINVAL; } if (!recv_data_size) return -EINVAL; if (receive_data->flags) { if (receive_data->flags & GB_UART_RECV_FLAG_BREAK) tty_flags = TTY_BREAK; else if (receive_data->flags & GB_UART_RECV_FLAG_PARITY) tty_flags = TTY_PARITY; else if (receive_data->flags & GB_UART_RECV_FLAG_FRAMING) tty_flags = TTY_FRAME; /* overrun is special, not associated with a char */ if (receive_data->flags & GB_UART_RECV_FLAG_OVERRUN) tty_insert_flip_char(port, 0, TTY_OVERRUN); } count = tty_insert_flip_string_fixed_flag(port, receive_data->data, tty_flags, recv_data_size); if (count != recv_data_size) { dev_err(&gb_tty->gbphy_dev->dev, "UART: RX 0x%08x bytes only wrote 0x%08x\n", recv_data_size, count); } if (count) tty_flip_buffer_push(port); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Bryan O'Donoghue15553.26%225.00%
Johan Hovold11037.80%112.50%
Alex Elder165.50%112.50%
Greg Kroah-Hartman72.41%337.50%
Sandeep Patil31.03%112.50%
Total291100.00%8100.00%


static int gb_uart_serial_state_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = gb_connection_get_data(connection); struct gb_message *request = op->request; struct gb_uart_serial_state_request *serial_state; if (request->payload_size < sizeof(*serial_state)) { dev_err(&gb_tty->gbphy_dev->dev, "short serial-state event received (%zu < %zu)\n", request->payload_size, sizeof(*serial_state)); return -EINVAL; } serial_state = request->payload; gb_tty->ctrlin = serial_state->control; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Johan Hovold5756.44%120.00%
Bryan O'Donoghue3938.61%120.00%
Greg Kroah-Hartman43.96%240.00%
Sandeep Patil10.99%120.00%
Total101100.00%5100.00%


static int gb_uart_receive_credits_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = gb_connection_get_data(connection); struct gb_message *request = op->request; struct gb_uart_receive_credits_request *credit_request; unsigned long flags; unsigned int incoming_credits; int ret = 0; if (request->payload_size < sizeof(*credit_request)) { dev_err(&gb_tty->gbphy_dev->dev, "short receive_credits event received (%zu < %zu)\n", request->payload_size, sizeof(*credit_request)); return -EINVAL; } credit_request = request->payload; incoming_credits = le16_to_cpu(credit_request->count); spin_lock_irqsave(&gb_tty->write_lock, flags); gb_tty->credits += incoming_credits; if (gb_tty->credits > GB_UART_FIRMWARE_CREDITS) { gb_tty->credits -= incoming_credits; ret = -EINVAL; } spin_unlock_irqrestore(&gb_tty->write_lock, flags); if (ret) { dev_err(&gb_tty->gbphy_dev->dev, "invalid number of incoming credits: %d\n", incoming_credits); return ret; } if (!gb_tty->close_pending) schedule_work(&gb_tty->tx_work); /* * the port the tty layer may be waiting for credits */ tty_port_tty_wakeup(&gb_tty->port); if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) complete(&gb_tty->credits_complete); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam225100.00%2100.00%
Total225100.00%2100.00%


static int gb_uart_request_handler(struct gb_operation *op) { struct gb_connection *connection = op->connection; struct gb_tty *gb_tty = gb_connection_get_data(connection); int type = op->type; int ret; switch (type) { case GB_UART_TYPE_RECEIVE_DATA: ret = gb_uart_receive_data_handler(op); break; case GB_UART_TYPE_SERIAL_STATE: ret = gb_uart_serial_state_handler(op); break; case GB_UART_TYPE_RECEIVE_CREDITS: ret = gb_uart_receive_credits_handler(op); break; default: dev_err(&gb_tty->gbphy_dev->dev, "unsupported unsolicited request: 0x%02x\n", type); ret = -EINVAL; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Bryan O'Donoghue4443.14%330.00%
Johan Hovold2524.51%220.00%
Greg Kroah-Hartman2019.61%220.00%
Axel Haslam1110.78%110.00%
Viresh Kumar10.98%110.00%
Sandeep Patil10.98%110.00%
Total102100.00%10100.00%


static void gb_uart_tx_write_work(struct work_struct *work) { struct gb_uart_send_data_request *request; struct gb_tty *gb_tty; unsigned long flags; unsigned int send_size; int ret; gb_tty = container_of(work, struct gb_tty, tx_work); request = gb_tty->buffer; while (1) { if (gb_tty->close_pending) break; spin_lock_irqsave(&gb_tty->write_lock, flags); send_size = gb_tty->buffer_payload_max; if (send_size > gb_tty->credits) send_size = gb_tty->credits; send_size = kfifo_out_peek(&gb_tty->write_fifo, &request->data[0], send_size); if (!send_size) { spin_unlock_irqrestore(&gb_tty->write_lock, flags); break; } gb_tty->credits -= send_size; spin_unlock_irqrestore(&gb_tty->write_lock, flags); request->size = cpu_to_le16(send_size); ret = gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_DATA, request, sizeof(*request) + send_size, NULL, 0); if (ret) { dev_err(&gb_tty->gbphy_dev->dev, "send data error: %d\n", ret); spin_lock_irqsave(&gb_tty->write_lock, flags); gb_tty->credits += send_size; spin_unlock_irqrestore(&gb_tty->write_lock, flags); if (!gb_tty->close_pending) schedule_work(work); return; } spin_lock_irqsave(&gb_tty->write_lock, flags); ret = kfifo_out(&gb_tty->write_fifo, &request->data[0], send_size); spin_unlock_irqrestore(&gb_tty->write_lock, flags); tty_port_tty_wakeup(&gb_tty->port); } }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam22176.74%228.57%
Greg Kroah-Hartman5218.06%228.57%
Bryan O'Donoghue144.86%228.57%
Viresh Kumar10.35%114.29%
Total288100.00%7100.00%


static int send_line_coding(struct gb_tty *tty) { struct gb_uart_set_line_coding_request request; memcpy(&request, &tty->line_coding, sizeof(tty->line_coding)); return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, &request, sizeof(request), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman5498.18%375.00%
Viresh Kumar11.82%125.00%
Total55100.00%4100.00%


static int send_control(struct gb_tty *gb_tty, u8 control) { struct gb_uart_set_control_line_state_request request; request.control = control; return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SET_CONTROL_LINE_STATE, &request, sizeof(request), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman4291.30%240.00%
Bryan O'Donoghue36.52%240.00%
Viresh Kumar12.17%120.00%
Total46100.00%5100.00%


static int send_break(struct gb_tty *gb_tty, u8 state) { struct gb_uart_set_break_request request; if ((state != 0) && (state != 1)) { dev_err(&gb_tty->gbphy_dev->dev, "invalid break state of %d\n", state); return -EINVAL; } request.state = state; return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_BREAK, &request, sizeof(request), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman7593.75%457.14%
Bryan O'Donoghue45.00%228.57%
Sandeep Patil11.25%114.29%
Total80100.00%7100.00%


static int gb_uart_wait_for_all_credits(struct gb_tty *gb_tty) { int ret; if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) return 0; ret = wait_for_completion_timeout(&gb_tty->credits_complete, msecs_to_jiffies(GB_UART_CREDIT_WAIT_TIMEOUT_MSEC)); if (!ret) { dev_err(&gb_tty->gbphy_dev->dev, "time out waiting for credits\n"); return -ETIMEDOUT; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam66100.00%1100.00%
Total66100.00%1100.00%


static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) { struct gb_uart_serial_flush_request request; request.flags = flags; return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_FLUSH_FIFOS, &request, sizeof(request), NULL, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam46100.00%1100.00%
Total46100.00%1100.00%


static struct gb_tty *get_gb_by_minor(unsigned int minor) { struct gb_tty *gb_tty; mutex_lock(&table_lock); gb_tty = idr_find(&tty_minors, minor); if (gb_tty) { mutex_lock(&gb_tty->mutex); if (gb_tty->disconnected) { mutex_unlock(&gb_tty->mutex); gb_tty = NULL; } else { tty_port_get(&gb_tty->port); mutex_unlock(&gb_tty->mutex); } } mutex_unlock(&table_lock); return gb_tty; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman9498.95%266.67%
Chaehyun Lim11.05%133.33%
Total95100.00%3100.00%


static int alloc_minor(struct gb_tty *gb_tty) { int minor; mutex_lock(&table_lock); minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); mutex_unlock(&table_lock); if (minor >= 0) gb_tty->minor = minor; return minor; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman4985.96%150.00%
Alex Elder814.04%150.00%
Total57100.00%2100.00%


static void release_minor(struct gb_tty *gb_tty) { int minor = gb_tty->minor; gb_tty->minor = 0; /* Maybe should use an invalid value instead */ mutex_lock(&table_lock); idr_remove(&tty_minors, minor); mutex_unlock(&table_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman3168.89%150.00%
Alex Elder1431.11%150.00%
Total45100.00%2100.00%


static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) { struct gb_tty *gb_tty; int retval; gb_tty = get_gb_by_minor(tty->index); if (!gb_tty) return -ENODEV; retval = tty_standard_install(driver, tty); if (retval) goto error; tty->driver_data = gb_tty; return 0; error: tty_port_put(&gb_tty->port); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman80100.00%1100.00%
Total80100.00%1100.00%


static int gb_tty_open(struct tty_struct *tty, struct file *file) { struct gb_tty *gb_tty = tty->driver_data; return tty_port_open(&gb_tty->port, tty, file); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman38100.00%1100.00%
Total38100.00%1100.00%


static void gb_tty_close(struct tty_struct *tty, struct file *file) { struct gb_tty *gb_tty = tty->driver_data; tty_port_close(&gb_tty->port, tty, file); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman37100.00%1100.00%
Total37100.00%1100.00%


static void gb_tty_cleanup(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; tty_port_put(&gb_tty->port); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman28100.00%1100.00%
Total28100.00%1100.00%


static void gb_tty_hangup(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; tty_port_hangup(&gb_tty->port); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman28100.00%1100.00%
Total28100.00%1100.00%


static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct gb_tty *gb_tty = tty->driver_data; count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count, &gb_tty->write_lock); if (count && !gb_tty->close_pending) schedule_work(&gb_tty->tx_work); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman3652.94%266.67%
Axel Haslam3247.06%133.33%
Total68100.00%3100.00%


static int gb_tty_write_room(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; unsigned long flags; int room; spin_lock_irqsave(&gb_tty->write_lock, flags); room = kfifo_avail(&gb_tty->write_fifo); spin_unlock_irqrestore(&gb_tty->write_lock, flags); room -= GB_UART_WRITE_ROOM_MARGIN; if (room < 0) return 0; return room; }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam5068.49%133.33%
Greg Kroah-Hartman1216.44%133.33%
Bryan O'Donoghue1115.07%133.33%
Total73100.00%3100.00%


static int gb_tty_chars_in_buffer(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; unsigned long flags; int chars; spin_lock_irqsave(&gb_tty->write_lock, flags); chars = kfifo_len(&gb_tty->write_fifo); if (gb_tty->credits < GB_UART_FIRMWARE_CREDITS) chars += GB_UART_FIRMWARE_CREDITS - gb_tty->credits; spin_unlock_irqrestore(&gb_tty->write_lock, flags); return chars; }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam6382.89%250.00%
Greg Kroah-Hartman1317.11%250.00%
Total76100.00%4100.00%


static int gb_tty_break_ctl(struct tty_struct *tty, int state) { struct gb_tty *gb_tty = tty->driver_data; return send_break(gb_tty, state ? 1 : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman35100.00%3100.00%
Total35100.00%3100.00%


static void gb_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old) { struct gb_tty *gb_tty = tty->driver_data; struct ktermios *termios = &tty->termios; struct gb_tty_line_coding newline; u8 newctrl = gb_tty->ctrlout; newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); newline.format = termios->c_cflag & CSTOPB ? GB_SERIAL_2_STOP_BITS : GB_SERIAL_1_STOP_BITS; newline.parity = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; switch (termios->c_cflag & CSIZE) { case CS5: newline.data_bits = 5; break; case CS6: newline.data_bits = 6; break; case CS7: newline.data_bits = 7; break; case CS8: default: newline.data_bits = 8; break; } /* FIXME: needs to clear unsupported bits in the termios */ gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); if (C_BAUD(tty) == B0) { newline.rate = gb_tty->line_coding.rate; newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); } if (newctrl != gb_tty->ctrlout) { gb_tty->ctrlout = newctrl; send_control(gb_tty, newctrl); } if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) newline.flow_control |= GB_SERIAL_AUTO_RTSCTS_EN; else newline.flow_control &= ~GB_SERIAL_AUTO_RTSCTS_EN; if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); send_line_coding(gb_tty); } }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman27785.76%333.33%
Axel Haslam3811.76%333.33%
Bryan O'Donoghue82.48%333.33%
Total323100.00%9100.00%


static int gb_tty_tiocmget(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | TIOCM_CTS; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman83100.00%2100.00%
Total83100.00%2100.00%


static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct gb_tty *gb_tty = tty->driver_data; u8 newctrl = gb_tty->ctrlout; set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; if (gb_tty->ctrlout == newctrl) return 0; gb_tty->ctrlout = newctrl; return send_control(gb_tty, newctrl); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman11499.13%375.00%
Bryan O'Donoghue10.87%125.00%
Total115100.00%4100.00%


static void gb_tty_throttle(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; unsigned char stop_char; int retval; if (I_IXOFF(tty)) { stop_char = STOP_CHAR(tty); retval = gb_tty_write(tty, &stop_char, 1); if (retval <= 0) return; } if (tty->termios.c_cflag & CRTSCTS) { gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; retval = send_control(gb_tty, gb_tty->ctrlout); } }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman92100.00%2100.00%
Total92100.00%2100.00%


static void gb_tty_unthrottle(struct tty_struct *tty) { struct gb_tty *gb_tty = tty->driver_data; unsigned char start_char; int retval; if (I_IXOFF(tty)) { start_char = START_CHAR(tty); retval = gb_tty_write(tty, &start_char, 1); if (retval <= 0) return; } if (tty->termios.c_cflag & CRTSCTS) { gb_tty->ctrlout |= GB_UART_CTRL_RTS; retval = send_control(gb_tty, gb_tty->ctrlout); } }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman91100.00%2100.00%
Total91100.00%2100.00%


static int get_serial_info(struct gb_tty *gb_tty, struct serial_struct __user *info) { struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_16550A; tmp.line = gb_tty->minor; tmp.xmit_fifo_size = 16; tmp.baud_base = 9600; tmp.close_delay = gb_tty->port.close_delay / 10; tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; if (copy_to_user(info, &tmp, sizeof(tmp))) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman116100.00%3100.00%
Total116100.00%3100.00%


static int set_serial_info(struct gb_tty *gb_tty, struct serial_struct __user *newinfo) { struct serial_struct new_serial; unsigned int closing_wait; unsigned int close_delay; int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; close_delay = new_serial.close_delay * 10; closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; mutex_lock(&gb_tty->port.mutex); if (!capable(CAP_SYS_ADMIN)) { if ((close_delay != gb_tty->port.close_delay) || (closing_wait != gb_tty->port.closing_wait)) retval = -EPERM; else retval = -EOPNOTSUPP; } else { gb_tty->port.close_delay = close_delay; gb_tty->port.closing_wait = closing_wait; } mutex_unlock(&gb_tty->port.mutex); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman16098.77%150.00%
Alex Elder21.23%150.00%
Total162100.00%2100.00%


static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) { int retval = 0; DECLARE_WAITQUEUE(wait, current); struct async_icount old; struct async_icount new; if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) return -EINVAL; do { spin_lock_irq(&gb_tty->read_lock); old = gb_tty->oldcount; new = gb_tty->iocount; gb_tty->oldcount = new; spin_unlock_irq(&gb_tty->read_lock); if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) break; if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) break; if ((arg & TIOCM_RI) && (old.rng != new.rng)) break; add_wait_queue(&gb_tty->wioctl, &wait); set_current_state(TASK_INTERRUPTIBLE); schedule(); remove_wait_queue(&gb_tty->wioctl, &wait); if (gb_tty->disconnected) { if (arg & TIOCM_CD) break; retval = -ENODEV; } else if (signal_pending(current)) { retval = -ERESTARTSYS; } } while (!retval); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman21596.85%133.33%
Alex Elder52.25%133.33%
Johan Hovold20.90%133.33%
Total222100.00%3100.00%


static int gb_tty_get_icount(struct tty_struct *tty, struct serial_icounter_struct *icount) { struct gb_tty *gb_tty = tty->driver_data; icount->dsr = gb_tty->iocount.dsr; icount->rng = gb_tty->iocount.rng; icount->dcd = gb_tty->iocount.dcd; icount->frame = gb_tty->iocount.frame; icount->overrun = gb_tty->iocount.overrun; icount->parity = gb_tty->iocount.parity; icount->brk = gb_tty->iocount.brk; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman8081.63%150.00%
Johan Hovold1818.37%150.00%
Total98100.00%2100.00%


static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct gb_tty *gb_tty = tty->driver_data; switch (cmd) { case TIOCGSERIAL: return get_serial_info(gb_tty, (struct serial_struct __user *)arg); case TIOCSSERIAL: return set_serial_info(gb_tty, (struct serial_struct __user *)arg); case TIOCMIWAIT: return wait_serial_change(gb_tty, arg); } return -ENOIOCTLCMD; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman83100.00%2100.00%
Total83100.00%2100.00%


static void gb_tty_dtr_rts(struct tty_port *port, int on) { struct gb_tty *gb_tty; u8 newctrl; gb_tty = container_of(port, struct gb_tty, port); newctrl = gb_tty->ctrlout; if (on) newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); else newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); gb_tty->ctrlout = newctrl; send_control(gb_tty, newctrl); }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam75100.00%1100.00%
Total75100.00%1100.00%


static int gb_tty_port_activate(struct tty_port *port, struct tty_struct *tty) { struct gb_tty *gb_tty; gb_tty = container_of(port, struct gb_tty, port); return gbphy_runtime_get_sync(gb_tty->gbphy_dev); }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam41100.00%1100.00%
Total41100.00%1100.00%


static void gb_tty_port_shutdown(struct tty_port *port) { struct gb_tty *gb_tty; unsigned long flags; int ret; gb_tty = container_of(port, struct gb_tty, port); gb_tty->close_pending = true; cancel_work_sync(&gb_tty->tx_work); spin_lock_irqsave(&gb_tty->write_lock, flags); kfifo_reset_out(&gb_tty->write_fifo); spin_unlock_irqrestore(&gb_tty->write_lock, flags); if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) goto out; ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); if (ret) { dev_err(&gb_tty->gbphy_dev->dev, "error flushing transmitter: %d\n", ret); } gb_uart_wait_for_all_credits(gb_tty); out: gb_tty->close_pending = false; gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); }

Contributors

PersonTokensPropCommitsCommitProp
Axel Haslam137100.00%4100.00%
Total137100.00%4100.00%

static const struct tty_operations gb_ops = { .install = gb_tty_install, .open = gb_tty_open, .close = gb_tty_close, .cleanup = gb_tty_cleanup, .hangup = gb_tty_hangup, .write = gb_tty_write, .write_room = gb_tty_write_room, .ioctl = gb_tty_ioctl, .throttle = gb_tty_throttle, .unthrottle = gb_tty_unthrottle, .chars_in_buffer = gb_tty_chars_in_buffer, .break_ctl = gb_tty_break_ctl, .set_termios = gb_tty_set_termios, .tiocmget = gb_tty_tiocmget, .tiocmset = gb_tty_tiocmset, .get_icount = gb_tty_get_icount, }; static const struct tty_port_operations gb_port_ops = { .dtr_rts = gb_tty_dtr_rts, .activate = gb_tty_port_activate, .shutdown = gb_tty_port_shutdown, };
static int gb_uart_probe(struct gbphy_device *gbphy_dev, const struct gbphy_device_id *id) { struct gb_connection *connection; size_t max_payload; struct gb_tty *gb_tty; struct device *tty_dev; int retval; int minor; gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); if (!gb_tty) return -ENOMEM; connection = gb_connection_create(gbphy_dev->bundle, le16_to_cpu(gbphy_dev->cport_desc->id), gb_uart_request_handler); if (IS_ERR(connection)) { retval = PTR_ERR(connection); goto exit_tty_free; } max_payload = gb_operation_get_payload_size_max(connection); if (max_payload < sizeof(struct gb_uart_send_data_request)) { retval = -EINVAL; goto exit_connection_destroy; } gb_tty->buffer_payload_max = max_payload - sizeof(struct gb_uart_send_data_request); gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); if (!gb_tty->buffer) { retval = -ENOMEM; goto exit_connection_destroy; } INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, GFP_KERNEL); if (retval) goto exit_buf_free; gb_tty->credits = GB_UART_FIRMWARE_CREDITS; init_completion(&gb_tty->credits_complete); minor = alloc_minor(gb_tty); if (minor < 0) { if (minor == -ENOSPC) { dev_err(&gbphy_dev->dev, "no more free minor numbers\n"); retval = -ENODEV; } else { retval = minor; } goto exit_kfifo_free; } gb_tty->minor = minor; spin_lock_init(&gb_tty->write_lock); spin_lock_init(&gb_tty->read_lock); init_waitqueue_head(&gb_tty->wioctl); mutex_init(&gb_tty->mutex); tty_port_init(&gb_tty->port); gb_tty->port.ops = &gb_port_ops; gb_tty->connection = connection; gb_tty->gbphy_dev = gbphy_dev; gb_connection_set_data(connection, gb_tty); gb_gbphy_set_data(gbphy_dev, gb_tty); retval = gb_connection_enable_tx(connection); if (retval) goto exit_release_minor; send_control(gb_tty, gb_tty->ctrlout); /* initialize the uart to be 9600n81 */ gb_tty->line_coding.rate = cpu_to_le32(9600); gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; gb_tty->line_coding.data_bits = 8; send_line_coding(gb_tty); retval = gb_connection_enable(connection); if (retval) goto exit_connection_disable; tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, &gbphy_dev->dev); if (IS_ERR(tty_dev)) { retval = PTR_ERR(tty_dev); goto exit_connection_disable; } gbphy_runtime_put_autosuspend(gbphy_dev); return 0; exit_connection_disable: gb_connection_disable(connection); exit_release_minor: release_minor(gb_tty); exit_kfifo_free: kfifo_free(&gb_tty->write_fifo); exit_buf_free: kfree(gb_tty->buffer); exit_connection_destroy: gb_connection_destroy(connection); exit_tty_free: kfree(gb_tty); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman33163.65%937.50%
Axel Haslam6211.92%520.83%
Bryan O'Donoghue5710.96%312.50%
Johan Hovold295.58%14.17%
Phong Tran173.27%28.33%
Alex Elder112.12%14.17%
Sandeep Patil101.92%14.17%
Viresh Kumar30.58%28.33%
Total520100.00%24100.00%


static void gb_uart_remove(struct gbphy_device *gbphy_dev) { struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); struct gb_connection *connection = gb_tty->connection; struct tty_struct *tty; int ret; ret = gbphy_runtime_get_sync(gbphy_dev); if (ret) gbphy_runtime_get_noresume(gbphy_dev); mutex_lock(&gb_tty->mutex); gb_tty->disconnected = true; wake_up_all(&gb_tty->wioctl); mutex_unlock(&gb_tty->mutex); tty = tty_port_tty_get(&gb_tty->port); if (tty) { tty_vhangup(tty); tty_kref_put(tty); } gb_connection_disable_rx(connection); tty_unregister_device(gb_tty_driver, gb_tty->minor); /* FIXME - free transmit / receive buffers */ gb_connection_disable(connection); tty_port_destroy(&gb_tty->port); gb_connection_destroy(connection); release_minor(gb_tty); kfifo_free(&gb_tty->write_fifo); kfree(gb_tty->buffer); kfree(gb_tty); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman11669.05%750.00%
Axel Haslam2716.07%214.29%
Bryan O'Donoghue2011.90%321.43%
Sandeep Patil42.38%17.14%
Viresh Kumar10.60%17.14%
Total168100.00%14100.00%


static int gb_tty_init(void) { int retval = 0; gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); if (IS_ERR(gb_tty_driver)) { pr_err("Can not allocate tty driver\n"); retval = -ENOMEM; goto fail_unregister_dev; } gb_tty_driver->driver_name = "gb"; gb_tty_driver->name = GB_NAME; gb_tty_driver->major = 0; gb_tty_driver->minor_start = 0; gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; gb_tty_driver->init_termios = tty_std_termios; gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(gb_tty_driver, &gb_ops); retval = tty_register_driver(gb_tty_driver); if (retval) { pr_err("Can not register tty driver: %d\n", retval); goto fail_put_gb_tty; } return 0; fail_put_gb_tty: put_tty_driver(gb_tty_driver); fail_unregister_dev: return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman12882.05%466.67%
Marti Bolivar2817.95%233.33%
Total156100.00%6100.00%


static void gb_tty_exit(void) { tty_unregister_driver(gb_tty_driver); put_tty_driver(gb_tty_driver); idr_destroy(&tty_minors); }

Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman24100.00%3100.00%
Total24100.00%3100.00%

static const struct gbphy_device_id gb_uart_id_table[] = { { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_UART) }, { }, }; MODULE_DEVICE_TABLE(gbphy, gb_uart_id_table); static struct gbphy_driver uart_driver = { .name = "uart", .probe = gb_uart_probe, .remove = gb_uart_remove, .id_table = gb_uart_id_table, };
static int gb_uart_driver_init(void) { int ret; ret = gb_tty_init(); if (ret) return ret; ret = gb_gbphy_register(&uart_driver); if (ret) { gb_tty_exit(); return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Viresh Kumar4189.13%240.00%
Alex Elder36.52%120.00%
Greg Kroah-Hartman12.17%120.00%
Sandeep Patil12.17%120.00%
Total46100.00%5100.00%

module_init(gb_uart_driver_init);
static void gb_uart_driver_exit(void) { gb_gbphy_deregister(&uart_driver); gb_tty_exit(); }

Contributors

PersonTokensPropCommitsCommitProp
Viresh Kumar1694.12%266.67%
Sandeep Patil15.88%133.33%
Total17100.00%3100.00%

module_exit(gb_uart_driver_exit); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Greg Kroah-Hartman289158.59%2129.58%
Axel Haslam115923.49%912.68%
Bryan O'Donoghue3907.90%1216.90%
Johan Hovold2464.99%57.04%
Viresh Kumar861.74%68.45%
Alex Elder751.52%1014.08%
Marti Bolivar380.77%22.82%
Sandeep Patil290.59%11.41%
Phong Tran170.34%22.82%
Ingo Molnar10.02%11.41%
Chaehyun Lim10.02%11.41%
Bhumika Goyal10.02%11.41%
Total4934100.00%71100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.