From: Tom Knot Date: Mon, 30 Apr 2018 12:12:44 +0000 (+0200) Subject: Updated to 0.07 X-Git-Url: http://git.graph-it.com/?a=commitdiff_plain;h=90b5739c4ef9d6af5f1ea1e63b14a1878c5daed2;p=graphit%2Funipi-kernel.git Updated to 0.07 - Cleaned up possible conflicts when running system queries and starting flashing process at the same time - Added TTY driver code, which is inactive for now as Mervis is not able to properly use it for unknown reasons - Fixed sched include properly --- diff --git a/modules/unipi/src/unipi_common.h b/modules/unipi/src/unipi_common.h index 4ea3748..3bac807 100644 --- a/modules/unipi/src/unipi_common.h +++ b/modules/unipi/src/unipi_common.h @@ -48,10 +48,10 @@ ***************/ #define NEURONSPI_SCHED_REQUIRED 1 // Older kernels do not require sched/types to be specifically imported -#ifdef NEURONSPI_SCHED_REQUIRED -#include +#if NEURONSPI_SCHED_REQUIRED > 0 + #include #endif -#define NEURONSPI_MAJOR_VERSIONSTRING "Development Beta Version 0.05:16:04:2018" +#define NEURONSPI_MAJOR_VERSIONSTRING "Development Beta Version 0.07:30:04:2018" #define NEURONSPI_MAX_DEVS 3 #define NEURONSPI_MAX_UART 128 diff --git a/modules/unipi/src/unipi_platform.c b/modules/unipi/src/unipi_platform.c index afd107a..7266711 100644 --- a/modules/unipi/src/unipi_platform.c +++ b/modules/unipi/src/unipi_platform.c @@ -1581,7 +1581,7 @@ int neuronspi_regmap_hw_reg_read(void *context, unsigned int reg, unsigned int * u8 *outp_buf; int write_length, i; write_length = neuronspi_spi_compose_single_register_read(reg, &inp_buf, &outp_buf); - if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1)) { + if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0)) { for (i = 0; i < write_length; i++) { outp_buf[i] = 0; } @@ -1606,7 +1606,7 @@ int neuronspi_regmap_hw_reg_write(void *context, unsigned int reg, unsigned int u8 *outp_buf; int write_length, i; write_length = neuronspi_spi_compose_single_register_write(reg, &inp_buf, &outp_buf, (val >> 8)); - if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1)) { + if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0)) { for (i = 0; i < write_length; i++) { outp_buf[i] = 0; } @@ -1637,7 +1637,7 @@ int neuronspi_regmap_hw_gather_write(void *context, const void *reg, size_t reg_ if (block_counter == (reg_size - 1) || ((mb_reg_buf[i] + 1) != mb_reg_buf[i + 1])) { write_length = neuronspi_spi_compose_multiple_register_write(block_counter, mb_reg_buf[i - block_counter], &inp_buf, &outp_buf, (u8*)(&mb_val_buf[i - block_counter])); - neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 125, 1); + neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 125, 1, 0); block_counter = 0; kfree(inp_buf); kfree(outp_buf); @@ -1757,7 +1757,7 @@ int neuronspi_regmap_hw_read(void *context, const void *reg_buf, size_t reg_size // Check for continuity and read the largest possible continuous block if (block_counter == ((reg_size / 2) - 1) || ((mb_reg_buf[i] + 1) != mb_reg_buf[i + 1])) { write_length = neuronspi_spi_compose_multiple_register_read(block_counter + 1, mb_reg_buf[i - block_counter], &inp_buf, &outp_buf); - if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 125, 1)) { + if (neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 125, 1, 0)) { for (j = 0; j < write_length; j++) { outp_buf[j] = 0; } diff --git a/modules/unipi/src/unipi_spi.c b/modules/unipi/src/unipi_spi.c index 1311f69..b2fcdcf 100644 --- a/modules/unipi/src/unipi_spi.c +++ b/modules/unipi/src/unipi_spi.c @@ -27,6 +27,7 @@ nologies #include "unipi_iio.h" #include "unipi_misc.h" #include "unipi_spi.h" +#include "unipi_tty.h" /******************** * Data Definitions * @@ -60,6 +61,7 @@ struct neuronspi_char_driver neuronspi_cdrv = }; struct mutex neuronspi_master_mutex; +struct spinlock* neuronspi_ldisc_spinlock; struct spinlock* neuronspi_spi_w_spinlock; u8 neuronspi_spi_w_flag = 1; u8 neuronspi_probe_count = 0; @@ -111,7 +113,6 @@ ssize_t neuronspi_read (struct file *file_p, char *buffer, size_t len, loff_t *o s32 result = 0; u8 device_index = 0; - u32 frequency = NEURONSPI_COMMON_FREQ; struct neuronspi_file_data* private_data; struct spi_device* spi_driver_data; struct neuronspi_driver_data* driver_data; @@ -248,7 +249,7 @@ ssize_t neuronspi_write (struct file *file_p, const char *buffer, size_t len, lo neuronspi_spi_w_flag = 1; spin_unlock(neuronspi_spi_w_spinlock); neuronspi_spi_send_message(spi_driver_data, &private_data->send_buf[NEURONSPI_HEADER_LENGTH], private_data->recv_buf, - transmit_len, frequency, delay, send_header); + transmit_len, frequency, delay, send_header, buffer[7]); mutex_unlock(&private_data->lock); return len; } @@ -294,7 +295,7 @@ s32 neuronspi_spi_uart_write(struct spi_device *spi, u8 *send_buf, u8 length, u8 frequency = NEURONSPI_SLOWER_FREQ; } if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi, message_buf, recv_buf, transmit_len, frequency, 65, 1); + neuronspi_spi_send_message(spi, message_buf, recv_buf, transmit_len, frequency, 65, 1, 0); } kfree(message_buf); kfree(recv_buf); @@ -337,7 +338,7 @@ void neuronspi_spi_uart_read(struct spi_device* spi, u8 *send_buf, u8 *recv_buf, #endif } if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi, send_buf, recv_buf, transmit_len, frequency, 65, 1); + neuronspi_spi_send_message(spi, send_buf, recv_buf, transmit_len, frequency, 65, 1, 0); } } @@ -362,7 +363,7 @@ void neuronspi_spi_set_irqs(struct spi_device* spi_dev, u16 to) crc2 = neuronspi_spi_crc(&message_buf[6], NEURONSPI_SPI_IRQ_SET_MESSAGE_LEN - 8, crc1); memcpy(&message_buf[NEURONSPI_SPI_IRQ_SET_MESSAGE_LEN - 2], &crc2, 2); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_IRQ_SET_MESSAGE_LEN, frequency, 65, 1); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_IRQ_SET_MESSAGE_LEN, frequency, 65, 1, 0); } kfree(message_buf); kfree(recv_buf); @@ -390,7 +391,7 @@ void neuronspi_spi_uart_set_cflag(struct spi_device* spi_dev, u8 port, u32 to) crc2 = neuronspi_spi_crc(&message_buf[6], NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN - 8, crc1); memcpy(&message_buf[NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN - 2], &crc2, 2); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 65, 1); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 65, 1, 0); } kfree(message_buf); kfree(recv_buf); @@ -410,7 +411,7 @@ void neuronspi_spi_uart_set_ldisc(struct spi_device* spi_dev, u8 port, u8 to) #endif neuronspi_spi_compose_single_register_write(503, &message_buf, &recv_buf, to); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 35, 1); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 35, 1, 0); } kfree(message_buf); kfree(recv_buf); @@ -428,7 +429,7 @@ u8 neuronspi_spi_uart_get_ldisc(struct spi_device* spi_dev, u8 port) } neuronspi_spi_compose_single_register_read(503, &message_buf, &recv_buf); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 35, 1); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_SET_CFLAG_MESSAGE_LEN, frequency, 35, 1, 0); } outp = recv_buf[MODBUS_FIRST_DATA_BYTE + 1]; #if NEURONSPI_DETAILED_DEBUG > 1 @@ -667,7 +668,7 @@ static s32 neuronspi_spi_watchdog(void *data) -int neuronspi_spi_send_message(struct spi_device* spi_dev, u8 *send_buf, u8 *recv_buf, s32 len, s32 freq, s32 delay, s32 send_header) +int neuronspi_spi_send_message(struct spi_device* spi_dev, u8 *send_buf, u8 *recv_buf, s32 len, s32 freq, s32 delay, s32 send_header, u8 lock_val) { s32 i = 0; int ret_code = 0; @@ -679,68 +680,73 @@ int neuronspi_spi_send_message(struct spi_device* spi_dev, u8 *send_buf, u8 *rec struct neuronspi_driver_data *d_data; struct spi_transfer* s_trans; mutex_lock(&neuronspi_master_mutex); - s_trans = kzalloc(sizeof(struct spi_transfer) * trans_count, GFP_KERNEL); + d_data = spi_get_drvdata(spi_dev); + if (d_data != NULL && d_data->reserved_device && lock_val != d_data->reserved_device) { + memset(&recv_buf, 0, len); + } else { + s_trans = kzalloc(sizeof(struct spi_transfer) * trans_count, GFP_KERNEL); #if NEURONSPI_DETAILED_DEBUG > 1 - printk(KERN_INFO "NEURONSPI: SPI Master Write, len:%d,\n %100ph\n", len, send_buf); + printk(KERN_INFO "NEURONSPI: SPI Master Write, len:%d,\n %100ph\n", len, send_buf); #endif - if (!send_header) { - trans_count -= 1; // one less transmission as the header is omitted - } - spi_message_init(&s_msg); - for (i = 0; i < trans_count; i++) { - memset(&(s_trans[i]), 0, sizeof(s_trans[i])); - s_trans[i].delay_usecs = 0; - s_trans[i].bits_per_word = 8; - s_trans[i].speed_hz = freq; - if (i == 0) { - s_trans[i].delay_usecs = NEURONSPI_EDGE_DELAY; - } else if (i == 1) { - s_trans[i].tx_buf = send_buf; - s_trans[i].rx_buf = recv_buf; - if (send_header) { - s_trans[i].delay_usecs = delay; - s_trans[i].len = NEURONSPI_FIRST_MESSAGE_LENGTH; - } else { - // If len is more than NEURONSPI_MAX_TX * i, then chunk len is NEURONSPI_MAX_TX, otherwise it's the remainder - s_trans[i].len = (len - (NEURONSPI_MAX_TX * i)) > 0 ? NEURONSPI_MAX_TX : len; - } - } else if (i == trans_count - 1) { - if (send_header) { - s_trans[i].tx_buf = &(send_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); - s_trans[i].rx_buf = &(recv_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); - s_trans[i].len = ((NEURONSPI_MAX_TX * (i - 1)) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 2))); - } else { - s_trans[i].tx_buf = &(send_buf[NEURONSPI_MAX_TX * (i - 1)]); - s_trans[i].rx_buf = &(recv_buf[NEURONSPI_MAX_TX * (i - 1)]); - s_trans[i].len = ((NEURONSPI_MAX_TX * i) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 1))); - } - s_trans[i].delay_usecs = NEURONSPI_LAST_TRANSFER_DELAY; - // If len is more than NEURONSPI_MAX_TX * i (+ optionally header), then chunk len is NEURONSPI_MAX_TX (+ optionally header), - // otherwise it's the remainder - } else { - if (send_header) { - s_trans[i].tx_buf = &(send_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); - s_trans[i].rx_buf = &(recv_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); - s_trans[i].len = ((NEURONSPI_MAX_TX * (i - 1)) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 2))); - } else { - s_trans[i].tx_buf = &(send_buf[NEURONSPI_MAX_TX * (i - 1)]); - s_trans[i].rx_buf = &(recv_buf[NEURONSPI_MAX_TX * (i - 1)]); - s_trans[i].len = ((NEURONSPI_MAX_TX * i) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 1))); - } - // If len is more than NEURONSPI_MAX_TX * i (+ optionally header), then chunk len is NEURONSPI_MAX_TX (+ optionally header), - // otherwise it's the remainder - } - spi_message_add_tail(&(s_trans[i]), &s_msg); - } - spi_sync(spi_dev, &s_msg); - for (i = 0; i < trans_count; i++) { - spi_transfer_del(&(s_trans[i])); - } + if (!send_header) { + trans_count -= 1; // one less transmission as the header is omitted + } + spi_message_init(&s_msg); + for (i = 0; i < trans_count; i++) { + memset(&(s_trans[i]), 0, sizeof(s_trans[i])); + s_trans[i].delay_usecs = 0; + s_trans[i].bits_per_word = 8; + s_trans[i].speed_hz = freq; + if (i == 0) { + s_trans[i].delay_usecs = NEURONSPI_EDGE_DELAY; + } else if (i == 1) { + s_trans[i].tx_buf = send_buf; + s_trans[i].rx_buf = recv_buf; + if (send_header) { + s_trans[i].delay_usecs = delay; + s_trans[i].len = NEURONSPI_FIRST_MESSAGE_LENGTH; + } else { + // If len is more than NEURONSPI_MAX_TX * i, then chunk len is NEURONSPI_MAX_TX, otherwise it's the remainder + s_trans[i].len = (len - (NEURONSPI_MAX_TX * i)) > 0 ? NEURONSPI_MAX_TX : len; + } + } else if (i == trans_count - 1) { + if (send_header) { + s_trans[i].tx_buf = &(send_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); + s_trans[i].rx_buf = &(recv_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); + s_trans[i].len = ((NEURONSPI_MAX_TX * (i - 1)) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 2))); + } else { + s_trans[i].tx_buf = &(send_buf[NEURONSPI_MAX_TX * (i - 1)]); + s_trans[i].rx_buf = &(recv_buf[NEURONSPI_MAX_TX * (i - 1)]); + s_trans[i].len = ((NEURONSPI_MAX_TX * i) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 1))); + } + s_trans[i].delay_usecs = NEURONSPI_LAST_TRANSFER_DELAY; + // If len is more than NEURONSPI_MAX_TX * i (+ optionally header), then chunk len is NEURONSPI_MAX_TX (+ optionally header), + // otherwise it's the remainder + } else { + if (send_header) { + s_trans[i].tx_buf = &(send_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); + s_trans[i].rx_buf = &(recv_buf[NEURONSPI_FIRST_MESSAGE_LENGTH + (NEURONSPI_MAX_TX * (i - 2))]); + s_trans[i].len = ((NEURONSPI_MAX_TX * (i - 1)) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 2))); + } else { + s_trans[i].tx_buf = &(send_buf[NEURONSPI_MAX_TX * (i - 1)]); + s_trans[i].rx_buf = &(recv_buf[NEURONSPI_MAX_TX * (i - 1)]); + s_trans[i].len = ((NEURONSPI_MAX_TX * i) - len) < 0 ? NEURONSPI_MAX_TX : (len - (NEURONSPI_MAX_TX * (i - 1))); + } + // If len is more than NEURONSPI_MAX_TX * i (+ optionally header), then chunk len is NEURONSPI_MAX_TX (+ optionally header), + // otherwise it's the remainder + } + spi_message_add_tail(&(s_trans[i]), &s_msg); + } + spi_sync(spi_dev, &s_msg); + for (i = 0; i < trans_count; i++) { + spi_transfer_del(&(s_trans[i])); + } + kfree(s_trans); #if NEURONSPI_DETAILED_DEBUG > 1 - printk(KERN_INFO "NEURONSPI: SPI Master Read - %d:\n\t%100ph\n\t%100ph\n\t%100ph\n\t%100ph\n", len,recv_buf, &recv_buf[64], - &recv_buf[128], &recv_buf[192]); + printk(KERN_INFO "NEURONSPI: SPI Master Read - %d:\n\t%100ph\n\t%100ph\n\t%100ph\n\t%100ph\n", len,recv_buf, &recv_buf[64], + &recv_buf[128], &recv_buf[192]); #endif - d_data = spi_get_drvdata(spi_dev); + } if (d_data == NULL || (d_data != NULL && !d_data->reserved_device)) { recv_crc1 = neuronspi_spi_crc(recv_buf, 4, 0); memcpy(&packet_crc, &recv_buf[4], 2); @@ -794,9 +800,7 @@ int neuronspi_spi_send_message(struct spi_device* spi_dev, u8 *send_buf, u8 *rec ret_code = 1; } } - mutex_unlock(&neuronspi_master_mutex); - kfree(s_trans); return ret_code; } @@ -850,7 +854,7 @@ void neuronspi_spi_led_set_brightness(struct spi_device* spi_dev, enum led_brigh crc1 = neuronspi_spi_crc(message_buf, 4, 0); memcpy(&message_buf[4], &crc1, 2); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_LED_SET_MESSAGE_LEN, frequency, 25, 0); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_LED_SET_MESSAGE_LEN, frequency, 25, 0, 0); } kfree(message_buf); kfree(recv_buf); @@ -907,9 +911,7 @@ s32 neuronspi_spi_probe(struct spi_device *spi) { const struct neuronspi_devtype *devtype; struct sched_param sched_param = { .sched_priority = MAX_RT_PRIO / 2 }; - struct neuronspi_driver_data *n_spi; - s32 ret, i, no_irq = 0; u8 uart_count = 0; n_spi = kzalloc(sizeof *n_spi, GFP_KERNEL); @@ -984,17 +986,17 @@ s32 neuronspi_spi_probe(struct spi_device *spi) n_spi->spi_driver = &neuronspi_spi_driver; memcpy(n_spi->send_buf, &NEURONSPI_PROBE_MESSAGE, NEURONSPI_PROBE_MESSAGE_LEN); - neuronspi_spi_send_message(spi, n_spi->send_buf, n_spi->first_probe_reply, NEURONSPI_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1); + neuronspi_spi_send_message(spi, n_spi->send_buf, n_spi->first_probe_reply, NEURONSPI_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1, 0); // Throw away the first message - the associated SPI Master is sometimes not properly initialised at this point i = 0; do { memcpy(n_spi->send_buf, &NEURONSPI_PROBE_MESSAGE, NEURONSPI_PROBE_MESSAGE_LEN); memset(n_spi->first_probe_reply, 0, NEURONSPI_PROBE_MESSAGE_LEN); - neuronspi_spi_send_message(spi, n_spi->send_buf, n_spi->first_probe_reply, NEURONSPI_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1); + neuronspi_spi_send_message(spi, n_spi->send_buf, n_spi->first_probe_reply, NEURONSPI_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1, 0); i++; if (i > 1000) { // Sanity check to prevent looping if we get constant UART/0x41 messages - ret = -4; + ret = -ENODEV; kfree(n_spi); printk(KERN_INFO "NEURONSPI: Probe did not detect a valid Neuron device on CS %d\n", spi->chip_select); return ret; @@ -1018,7 +1020,7 @@ s32 neuronspi_spi_probe(struct spi_device *spi) } } } else if (!n_spi->probe_always_succeeds) { - ret = -5; + ret = -ENODEV; kfree(n_spi); printk(KERN_INFO "NEURONSPI: Probe did not detect a valid Neuron device on CS %d\n", spi->chip_select); return ret; @@ -1374,7 +1376,7 @@ u32 neuronspi_spi_uart_get_cflag(struct spi_device* spi_dev, u8 port) crc2 = neuronspi_spi_crc(&message_buf[6], NEURONSPI_SPI_UART_GET_CFLAG_MESSAGE_LEN - 8, crc1); memcpy(&message_buf[NEURONSPI_SPI_UART_GET_CFLAG_MESSAGE_LEN - 2], &crc2, 2); if (!d_data->reserved_device) { - neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_GET_CFLAG_MESSAGE_LEN, frequency, 65, 1); + neuronspi_spi_send_message(spi_dev, message_buf, recv_buf, NEURONSPI_SPI_UART_GET_CFLAG_MESSAGE_LEN, frequency, 65, 1, 0); } ret = ((u32*)recv_buf)[5]; #if NEURONSPI_DETAILED_DEBUG > 0 @@ -1550,6 +1552,7 @@ static s32 __init neuronspi_init(void) if (neuronspi_invalidate_thread != NULL) { wake_up_process(neuronspi_invalidate_thread); } + //neuronspi_tty_init(); return ret; } diff --git a/modules/unipi/src/unipi_spi.h b/modules/unipi/src/unipi_spi.h index 18fef46..83d88a7 100644 --- a/modules/unipi/src/unipi_spi.h +++ b/modules/unipi/src/unipi_spi.h @@ -168,7 +168,7 @@ s32 char_unregister_driver(void); irqreturn_t neuronspi_spi_irq(s32 irq, void *dev_id); s32 neuronspi_spi_probe(struct spi_device *spi); s32 neuronspi_spi_remove(struct spi_device *spi); -int neuronspi_spi_send_message(struct spi_device *spi_dev, u8 *send_buf, u8 *recv_buf, s32 len, s32 freq, s32 delay, s32 send_header); +int neuronspi_spi_send_message(struct spi_device *spi_dev, u8 *send_buf, u8 *recv_buf, s32 len, s32 freq, s32 delay, s32 send_header, u8 lock_val); s32 neuronspi_spi_uart_write(struct spi_device *spi, u8 *send_buf, u8 length, u8 uart_index); void neuronspi_spi_uart_read(struct spi_device* spi_dev, u8 *send_buf, u8 *recv_buf, s32 len, u8 uart_index); void neuronspi_spi_set_irqs(struct spi_device* spi_dev, u16 to); diff --git a/modules/unipi/src/unipi_tty.c b/modules/unipi/src/unipi_tty.c index 9037c4d..8cc65f4 100644 --- a/modules/unipi/src/unipi_tty.c +++ b/modules/unipi/src/unipi_tty.c @@ -12,4 +12,2664 @@ * */ +#include "unipi_tty.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* number of characters left in xmit buffer before select has we have room */ +#define WAKEUP_CHARS 256 + +/* + * This defines the low- and high-watermarks for throttling and + * unthrottling the TTY driver. These watermarks are used for + * controlling the space in the read buffer. + */ +#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ +#define TTY_THRESHOLD_UNTHROTTLE 128 + +/* + * Special byte codes used in the echo buffer to represent operations + * or special handling of characters. Bytes in the echo buffer that + * are not part of such special blocks are treated as normal character + * codes. + */ +#define ECHO_OP_START 0xff +#define ECHO_OP_MOVE_BACK_COL 0x80 +#define ECHO_OP_SET_CANON_COL 0x81 +#define ECHO_OP_ERASE_TAB 0x82 + +#define ECHO_COMMIT_WATERMARK 256 +#define ECHO_BLOCK 256 +#define ECHO_DISCARD_WATERMARK N_TTY_BUF_SIZE - (ECHO_BLOCK + 32) + + +#undef N_TTY_TRACE +#ifdef N_TTY_TRACE +# define n_tty_trace(f, args...) trace_printk(f, ##args) +#else +# define n_tty_trace(f, args...) +#endif + +struct neuronspi_tty_data { + /* producer-published */ + size_t read_head; + size_t commit_head; + size_t canon_head; + size_t echo_head; + size_t echo_commit; + size_t echo_mark; + DECLARE_BITMAP(char_map, 256); + + /* private to n_tty_receive_overrun (single-threaded) */ + unsigned long overrun_time; + int num_overrun; + + /* non-atomic */ + bool no_room; + + /* must hold exclusive termios_rwsem to reset these */ + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char push:1; + + /* shared by producer and consumer */ + char read_buf[N_TTY_BUF_SIZE]; + DECLARE_BITMAP(read_flags, N_TTY_BUF_SIZE); + unsigned char echo_buf[N_TTY_BUF_SIZE]; + + /* consumer-published */ + size_t read_tail; + size_t line_start; + + /* protected by output lock */ + unsigned int column; + unsigned int canon_column; + size_t echo_tail; + + struct mutex atomic_read_lock; + struct mutex output_lock; +}; + +static DEFINE_SPINLOCK(redirect_lock); +static struct file *redirect; + +static inline struct tty_struct *file_tty(struct file *file) +{ + return ((struct tty_file_private *)file->private_data)->tty; +} + +static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, + const char *routine) +{ +#ifdef TTY_PARANOIA_CHECK + if (!tty) { + pr_warn("(%d:%d): %s: NULL tty\n", + imajor(inode), iminor(inode), routine); + return 1; + } + if (tty->magic != TTY_MAGIC) { + pr_warn("(%d:%d): %s: bad magic number\n", + imajor(inode), iminor(inode), routine); + return 1; + } +#endif + return 0; +} + +//static ssize_t hung_up_tty_read(struct file *file, char __user *buf, +// size_t count, loff_t *ppos) +//{ +// return 0; +//} + +static ssize_t hung_up_tty_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static void tty_write_unlock(struct tty_struct *tty) +{ + mutex_unlock(&tty->atomic_write_lock); + wake_up_interruptible_poll(&tty->write_wait, POLLOUT); +} + +static int tty_write_lock(struct tty_struct *tty, int ndelay) +{ + if (!mutex_trylock(&tty->atomic_write_lock)) { + if (ndelay) + return -EAGAIN; + if (mutex_lock_interruptible(&tty->atomic_write_lock)) + return -ERESTARTSYS; + } + return 0; +} + +static int is_ignored(int sig) +{ + return (sigismember(¤t->blocked, sig) || + current->sighand->action[sig-1].sa.sa_handler == SIG_IGN); +} + +int __tty_check_change(struct tty_struct *tty, int sig) +{ + unsigned long flags; + struct pid *pgrp, *tty_pgrp; + int ret = 0; + + if (current->signal->tty != tty) + return 0; + + rcu_read_lock(); + pgrp = task_pgrp(current); + + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty_pgrp = tty->pgrp; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + + if (tty_pgrp && pgrp != tty->pgrp) { + if (is_ignored(sig)) { + if (sig == SIGTTIN) + ret = -EIO; + } //else if (is_current_pgrp_orphaned()) + //ret = -EIO; + else { + kill_pgrp(pgrp, sig, 1); + set_thread_flag(TIF_SIGPENDING); + ret = -ERESTARTSYS; + } + } + rcu_read_unlock(); + + if (!tty_pgrp) + tty_warn(tty, "sig=%d, tty->pgrp == NULL!\n", sig); + + return ret; +} + +static void tty_update_time(struct timespec *time) +{ + unsigned long sec = get_seconds(); + + /* + * We only care if the two values differ in anything other than the + * lower three bits (i.e every 8 seconds). If so, then we can update + * the time of the tty device, otherwise it could be construded as a + * security leak to let userspace know the exact timing of the tty. + */ + if ((sec ^ time->tv_sec) & ~7) + time->tv_sec = sec; +} + + +static inline ssize_t do_tty_write( + ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), + struct tty_struct *tty, + struct file *file, + const char __user *buf, + size_t count) +{ + ssize_t ret, written = 0; + unsigned int chunk; + + ret = tty_write_lock(tty, file->f_flags & O_NDELAY); + if (ret < 0) + return ret; + + /* + * We chunk up writes into a temporary buffer. This + * simplifies low-level drivers immensely, since they + * don't have locking issues and user mode accesses. + * + * But if TTY_NO_WRITE_SPLIT is set, we should use a + * big chunk-size.. + * + * The default chunk-size is 2kB, because the NTTY + * layer has problems with bigger chunks. It will + * claim to be able to handle more characters than + * it actually does. + * + * FIXME: This can probably go away now except that 64K chunks + * are too likely to fail unless switched to vmalloc... + */ + chunk = 2048; + if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags)) + chunk = 65536; + if (count < chunk) + chunk = count; + + /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ + if (tty->write_cnt < chunk) { + unsigned char *buf_chunk; + + if (chunk < 1024) + chunk = 1024; + + buf_chunk = kmalloc(chunk, GFP_KERNEL); + if (!buf_chunk) { + ret = -ENOMEM; + goto out; + } + kfree(tty->write_buf); + tty->write_cnt = chunk; + tty->write_buf = buf_chunk; + } + + /* Do the write .. */ + for (;;) { + size_t size = count; + if (size > chunk) + size = chunk; + ret = -EFAULT; + if (copy_from_user(tty->write_buf, buf, size)) + break; + ret = write(tty, file, tty->write_buf, size); + if (ret <= 0) + break; + written += ret; + buf += ret; + count -= ret; + if (!count) + break; + ret = -ERESTARTSYS; + if (signal_pending(current)) + break; + cond_resched(); + } + if (written) { + tty_update_time(&file_inode(file)->i_mtime); + ret = written; + } +out: + tty_write_unlock(tty); + return ret; +} + +static ssize_t tty_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct tty_struct *tty = file_tty(file); + struct tty_ldisc *ld; + ssize_t ret; + + if (tty_paranoia_check(tty, file_inode(file), "tty_write")) + return -EIO; + if (!tty || !tty->ops->write || tty_io_error(tty)) + return -EIO; + /* Short term debug to catch buggy drivers */ + if (tty->ops->write_room == NULL) + tty_err(tty, "missing write_room method\n"); + ld = tty_ldisc_ref_wait(tty); + if (!ld) + return hung_up_tty_write(file, buf, count, ppos); + if (!ld->ops->write) + ret = -EIO; + else + ret = do_tty_write(ld->ops->write, tty, file, buf, count); + tty_ldisc_deref(ld); + return ret; +} + + +const char *tty_driver_name(const struct tty_struct *tty) +{ + if (!tty || !tty->driver) + return ""; + return tty->driver->name; +} + +int tty_throttle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->throttle_mutex); + if (!tty_throttled(tty)) { + if (tty->flow_change != TTY_THROTTLE_SAFE) + ret = 1; + else { + set_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->throttle) + tty->ops->throttle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + +/** + * tty_unthrottle_safe - flow control + * @tty: terminal + * + * Similar to tty_unthrottle() but will only attempt unthrottle + * if tty->flow_change is TTY_UNTHROTTLE_SAFE. Prevents an accidental + * unthrottle due to race conditions when unthrottling is conditional + * on factors evaluated prior to unthrottling. + * + * Returns 0 if tty is unthrottled (or was already unthrottled) + */ + +int tty_unthrottle_safe(struct tty_struct *tty) +{ + int ret = 0; + + mutex_lock(&tty->throttle_mutex); + if (tty_throttled(tty)) { + if (tty->flow_change != TTY_UNTHROTTLE_SAFE) + ret = 1; + else { + clear_bit(TTY_THROTTLED, &tty->flags); + if (tty->ops->unthrottle) + tty->ops->unthrottle(tty); + } + } + mutex_unlock(&tty->throttle_mutex); + + return ret; +} + +bool tty_buffer_restart_work(struct tty_port *port) +{ + return queue_work(system_unbound_wq, &port->buf.work); +} + +bool tty_buffer_cancel_work(struct tty_port *port) +{ + return cancel_work_sync(&port->buf.work); +} + +ssize_t redirected_tty_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct file *p = NULL; + + spin_lock(&redirect_lock); + if (redirect) + p = get_file(redirect); + spin_unlock(&redirect_lock); + + if (p) { + ssize_t res; + res = vfs_write(p, buf, count, &p->f_pos); + fput(p); + return res; + } + return tty_write(file, buf, count, ppos); +} + +void tty_buffer_flush_work(struct tty_port *port) +{ + flush_work(&port->buf.work); +} + +static inline size_t read_cnt(struct neuronspi_tty_data *ldata) +{ + return ldata->read_head - ldata->read_tail; +} + +static inline unsigned char read_buf(struct neuronspi_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *read_buf_addr(struct neuronspi_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char echo_buf(struct neuronspi_tty_data *ldata, size_t i) +{ + return ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static inline unsigned char *echo_buf_addr(struct neuronspi_tty_data *ldata, size_t i) +{ + return &ldata->echo_buf[i & (N_TTY_BUF_SIZE - 1)]; +} + +static int tty_copy_to_user(struct tty_struct *tty, void __user *to, + size_t tail, size_t n) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + size_t size = N_TTY_BUF_SIZE - tail; + const void *from = read_buf_addr(ldata, tail); + int uncopied; + + if (n > size) { + tty_audit_add_data(tty, from, size); + uncopied = copy_to_user(to, from, size); + if (uncopied) + return uncopied; + to += size; + n -= size; + from = ldata->read_buf; + } + + tty_audit_add_data(tty, from, n); + return copy_to_user(to, from, n); +} + +/** + * n_tty_kick_worker - start input worker (if required) + * @tty: terminal + * + * Re-schedules the flip buffer work if it may have stopped + * + * Caller holds exclusive termios_rwsem + * or + * n_tty_read()/consumer path: + * holds non-exclusive termios_rwsem + */ + +static void neuronspi_tty_kick_worker(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + /* Did the input worker stop? Restart it */ + if (unlikely(ldata->no_room)) { + ldata->no_room = 0; + + WARN_RATELIMIT(tty->port->itty == NULL, + "scheduling with invalid itty\n"); + /* see if ldisc has been killed - if so, this means that + * even though the ldisc has been halted and ->buf.work + * cancelled, ->buf.work is about to be rescheduled + */ + WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), + "scheduling buffer work for halted ldisc\n"); + tty_buffer_restart_work(tty->port); + } +} + +static ssize_t chars_in_buffer(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + ssize_t n = 0; + + if (!ldata->icanon) + n = ldata->commit_head - ldata->read_tail; + else + n = ldata->canon_head - ldata->read_tail; + return n; +} + +/** + * n_tty_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void neuronspi_tty_write_wakeup(struct tty_struct *tty) +{ + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + kill_fasync(&tty->fasync, SIGIO, POLL_OUT); +} + +static void neuronspi_tty_check_throttle(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + /* + * Check the remaining room for the input canonicalization + * mode. We don't want to throttle the driver if we're in + * canonical mode and don't have a newline yet! + */ + if (ldata->icanon && ldata->canon_head == ldata->read_tail) + return; + + while (1) { + int throttled; + tty_set_flow_change(tty, TTY_THROTTLE_SAFE); + if (N_TTY_BUF_SIZE - read_cnt(ldata) >= TTY_THRESHOLD_THROTTLE) + break; + throttled = tty_throttle_safe(tty); + if (!throttled) + break; + } + __tty_set_flow_change(tty, 0); +} + +static void neuronspi_tty_check_unthrottle(struct tty_struct *tty) +{ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + return; + neuronspi_tty_kick_worker(tty); + tty_wakeup(tty->link); + return; + } + + /* If there is enough space in the read buffer now, let the + * low-level driver know. We use chars_in_buffer() to + * check the buffer, as it now knows about canonical mode. + * Otherwise, if the driver is throttled and the line is + * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, + * we won't get any more characters. + */ + + while (1) { + int unthrottled; + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + if (chars_in_buffer(tty) > TTY_THRESHOLD_UNTHROTTLE) + break; + neuronspi_tty_kick_worker(tty); + unthrottled = tty_unthrottle_safe(tty); + if (!unthrottled) + break; + } + __tty_set_flow_change(tty, 0); +} + +/** + * put_tty_queue - add character to tty + * @c: character + * @ldata: n_tty data + * + * Add a character to the tty read_buf queue. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + */ + +static inline void put_tty_queue(unsigned char c, struct neuronspi_tty_data *ldata) +{ + *read_buf_addr(ldata, ldata->read_head) = c; + ldata->read_head++; +} + +/** + * reset_buffer_flags - reset buffer state + * @tty: terminal to reset + * + * Reset the read buffer counters and clear the flags. + * Called from n_tty_open() and n_tty_flush_buffer(). + * + * Locking: caller holds exclusive termios_rwsem + * (or locking is not required) + */ + +static void reset_buffer_flags(struct neuronspi_tty_data *ldata) +{ + ldata->read_head = ldata->canon_head = ldata->read_tail = 0; + ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; + ldata->commit_head = 0; + ldata->echo_mark = 0; + ldata->line_start = 0; + + ldata->erasing = 0; + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->push = 0; +} + +static void neuronspi_tty_packet_mode_flush(struct tty_struct *tty) +{ + unsigned long flags; + + if (tty->link->packet) { + spin_lock_irqsave(&tty->ctrl_lock, flags); + tty->ctrl_status |= TIOCPKT_FLUSHREAD; + spin_unlock_irqrestore(&tty->ctrl_lock, flags); + wake_up_interruptible(&tty->link->read_wait); + } +} + +/** + * n_tty_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the tty layer wants the + * buffer flushed (eg at hangup) or when the N_TTY line discipline + * internally has to clean the pending queue (for example some signals). + * + * Holds termios_rwsem to exclude producer/consumer while + * buffer indices are reset. + * + * Locking: ctrl_lock, exclusive termios_rwsem + */ + +static void neuronspi_tty_flush_buffer(struct tty_struct *tty) +{ + printk(KERN_INFO "TTYFlushBuffer Called"); + down_write(&tty->termios_rwsem); + reset_buffer_flags(tty->disc_data); + neuronspi_tty_kick_worker(tty); + + if (tty->link) + neuronspi_tty_packet_mode_flush(tty); + up_write(&tty->termios_rwsem); +} + +/** + * is_utf8_continuation - utf8 multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character. We use this to correctly compute the on screen size + * of the character when printing + */ + +static inline int is_utf8_continuation(unsigned char c) +{ + return (c & 0xc0) == 0x80; +} + +/** + * is_continuation - multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character and the terminal is in unicode mode. + */ + +static inline int is_continuation(unsigned char c, struct tty_struct *tty) +{ + return I_IUTF8(tty) && is_utf8_continuation(c); +} + +/** + * do_output_char - output one character + * @c: character (or partial unicode symbol) + * @tty: terminal device + * @space: space available in tty driver write buffer + * + * This is a helper function that handles one output character + * (including special characters like TAB, CR, LF, etc.), + * doing OPOST processing and putting the results in the + * tty driver's write buffer. + * + * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY + * and NLDLY. They simply aren't relevant in the world today. + * If you ever need them, add them here. + * + * Returns the number of bytes of buffer space used or -1 if + * no space left. + * + * Locking: should be called under the output_lock to protect + * the column state and space left in the buffer + */ + +static int do_output_char(unsigned char c, struct tty_struct *tty, int space) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int spaces; + + if (!space) + return -1; + + switch (c) { + case '\n': + if (O_ONLRET(tty)) + ldata->column = 0; + if (O_ONLCR(tty)) { + if (space < 2) + return -1; + ldata->canon_column = ldata->column = 0; + tty->ops->write(tty, "\r\n", 2); + return 2; + } + ldata->canon_column = ldata->column; + break; + case '\r': + if (O_ONOCR(tty) && ldata->column == 0) + return 0; + if (O_OCRNL(tty)) { + c = '\n'; + if (O_ONLRET(tty)) + ldata->canon_column = ldata->column = 0; + break; + } + ldata->canon_column = ldata->column = 0; + break; + case '\t': + spaces = 8 - (ldata->column & 7); + if (O_TABDLY(tty) == XTABS) { + if (space < spaces) + return -1; + ldata->column += spaces; + tty->ops->write(tty, " ", spaces); + return spaces; + } + ldata->column += spaces; + break; + case '\b': + if (ldata->column > 0) + ldata->column--; + break; + default: + if (!iscntrl(c)) { + if (O_OLCUC(tty)) + c = toupper(c); + if (!is_continuation(c, tty)) + ldata->column++; + } + break; + } + + tty_put_char(tty, c); + return 1; +} + +/** + * process_output - output post processor + * @c: character (or partial unicode symbol) + * @tty: terminal device + * + * Output one character with OPOST processing. + * Returns -1 when the output device is full and the character + * must be retried. + * + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) + */ + +static int process_output(unsigned char c, struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int space, retval; + + mutex_lock(&ldata->output_lock); + + space = tty_write_room(tty); + retval = do_output_char(c, tty, space); + + mutex_unlock(&ldata->output_lock); + if (retval < 0) + return -1; + else + return 0; +} + +/** + * process_output_block - block post processor + * @tty: terminal device + * @buf: character buffer + * @nr: number of bytes to output + * + * Output a block of characters with OPOST processing. + * Returns the number of characters output. + * + * This path is used to speed up block console writes, among other + * things when processing blocks of output data. It handles only + * the simple cases normally found and helps to generate blocks of + * symbols for the console driver and thus improve performance. + * + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) + */ + +static ssize_t process_output_block(struct tty_struct *tty, + const unsigned char *buf, unsigned int nr) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int space; + int i; + const unsigned char *cp; + + mutex_lock(&ldata->output_lock); + + space = tty_write_room(tty); + if (!space) { + mutex_unlock(&ldata->output_lock); + return 0; + } + if (nr > space) + nr = space; + + for (i = 0, cp = buf; i < nr; i++, cp++) { + unsigned char c = *cp; + + switch (c) { + case '\n': + if (O_ONLRET(tty)) + ldata->column = 0; + if (O_ONLCR(tty)) + goto break_out; + ldata->canon_column = ldata->column; + break; + case '\r': + if (O_ONOCR(tty) && ldata->column == 0) + goto break_out; + if (O_OCRNL(tty)) + goto break_out; + ldata->canon_column = ldata->column = 0; + break; + case '\t': + goto break_out; + case '\b': + if (ldata->column > 0) + ldata->column--; + break; + default: + if (!iscntrl(c)) { + if (O_OLCUC(tty)) + goto break_out; + if (!is_continuation(c, tty)) + ldata->column++; + } + break; + } + } +break_out: + i = tty->ops->write(tty, buf, i); + + mutex_unlock(&ldata->output_lock); + return i; +} + +/** + * process_echoes - write pending echo characters + * @tty: terminal device + * + * Write previously buffered echo (and other ldisc-generated) + * characters to the tty. + * + * Characters generated by the ldisc (including echoes) need to + * be buffered because the driver's write buffer can fill during + * heavy program output. Echoing straight to the driver will + * often fail under these conditions, causing lost characters and + * resulting mismatches of ldisc state information. + * + * Since the ldisc state must represent the characters actually sent + * to the driver at the time of the write, operations like certain + * changes in column state are also saved in the buffer and executed + * here. + * + * A circular fifo buffer is used so that the most recent characters + * are prioritized. Also, when control characters are echoed with a + * prefixed "^", the pair is treated atomically and thus not separated. + * + * Locking: callers must hold output_lock + */ + +static size_t __process_echoes(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int space, old_space; + size_t tail; + unsigned char c; + + old_space = space = tty_write_room(tty); + + tail = ldata->echo_tail; + while (ldata->echo_commit != tail) { + c = echo_buf(ldata, tail); + if (c == ECHO_OP_START) { + unsigned char op; + int no_space_left = 0; + + /* + * If the buffer byte is the start of a multi-byte + * operation, get the next byte, which is either the + * op code or a control character value. + */ + op = echo_buf(ldata, tail + 1); + + switch (op) { + unsigned int num_chars, num_bs; + + case ECHO_OP_ERASE_TAB: + num_chars = echo_buf(ldata, tail + 2); + + /* + * Determine how many columns to go back + * in order to erase the tab. + * This depends on the number of columns + * used by other characters within the tab + * area. If this (modulo 8) count is from + * the start of input rather than from a + * previous tab, we offset by canon column. + * Otherwise, tab spacing is normal. + */ + if (!(num_chars & 0x80)) + num_chars += ldata->canon_column; + num_bs = 8 - (num_chars & 7); + + if (num_bs > space) { + no_space_left = 1; + break; + } + space -= num_bs; + while (num_bs--) { + tty_put_char(tty, '\b'); + if (ldata->column > 0) + ldata->column--; + } + tail += 3; + break; + + case ECHO_OP_SET_CANON_COL: + ldata->canon_column = ldata->column; + tail += 2; + break; + + case ECHO_OP_MOVE_BACK_COL: + if (ldata->column > 0) + ldata->column--; + tail += 2; + break; + + case ECHO_OP_START: + /* This is an escaped echo op start code */ + if (!space) { + no_space_left = 1; + break; + } + tty_put_char(tty, ECHO_OP_START); + ldata->column++; + space--; + tail += 2; + break; + + default: + /* + * If the op is not a special byte code, + * it is a ctrl char tagged to be echoed + * as "^X" (where X is the letter + * representing the control char). + * Note that we must ensure there is + * enough space for the whole ctrl pair. + * + */ + if (space < 2) { + no_space_left = 1; + break; + } + tty_put_char(tty, '^'); + tty_put_char(tty, op ^ 0100); + ldata->column += 2; + space -= 2; + tail += 2; + } + + if (no_space_left) + break; + } else { + if (O_OPOST(tty)) { + int retval = do_output_char(c, tty, space); + if (retval < 0) + break; + space -= retval; + } else { + if (!space) + break; + tty_put_char(tty, c); + space -= 1; + } + tail += 1; + } + } + + /* If the echo buffer is nearly full (so that the possibility exists + * of echo overrun before the next commit), then discard enough + * data at the tail to prevent a subsequent overrun */ + while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { + if (echo_buf(ldata, tail) == ECHO_OP_START) { + if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) + tail += 3; + else + tail += 2; + } else + tail++; + } + + ldata->echo_tail = tail; + return old_space - space; +} + +static void commit_echoes(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + size_t nr, old, echoed; + size_t head; + + head = ldata->echo_head; + ldata->echo_mark = head; + old = ldata->echo_commit - ldata->echo_tail; + + /* Process committed echoes if the accumulated # of bytes + * is over the threshold (and try again each time another + * block is accumulated) */ + nr = head - ldata->echo_tail; + if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = head; + echoed = __process_echoes(tty); + mutex_unlock(&ldata->output_lock); + + if (echoed && tty->ops->flush_chars) + tty->ops->flush_chars(tty); +} + +static void process_echoes(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + size_t echoed; + + if (ldata->echo_mark == ldata->echo_tail) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = ldata->echo_mark; + echoed = __process_echoes(tty); + mutex_unlock(&ldata->output_lock); + + if (echoed && tty->ops->flush_chars) + tty->ops->flush_chars(tty); +} + +/* NB: echo_mark and echo_head should be equivalent here */ +static void flush_echoes(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if ((!L_ECHO(tty) && !L_ECHONL(tty)) || + ldata->echo_commit == ldata->echo_head) + return; + + mutex_lock(&ldata->output_lock); + ldata->echo_commit = ldata->echo_head; + __process_echoes(tty); + mutex_unlock(&ldata->output_lock); +} + +/** + * add_echo_byte - add a byte to the echo buffer + * @c: unicode byte to echo + * @ldata: n_tty data + * + * Add a character or operation byte to the echo buffer. + */ + +static inline void add_echo_byte(unsigned char c, struct neuronspi_tty_data *ldata) +{ + *echo_buf_addr(ldata, ldata->echo_head++) = c; +} + +/** + * echo_move_back_col - add operation to move back a column + * @ldata: n_tty data + * + * Add an operation to the echo buffer to move back one column. + */ + +static void echo_move_back_col(struct neuronspi_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); +} + +/** + * echo_set_canon_col - add operation to set the canon column + * @ldata: n_tty data + * + * Add an operation to the echo buffer to set the canon column + * to the current column. + */ + +static void echo_set_canon_col(struct neuronspi_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); +} + +/** + * echo_erase_tab - add operation to erase a tab + * @num_chars: number of character columns already used + * @after_tab: true if num_chars starts after a previous tab + * @ldata: n_tty data + * + * Add an operation to the echo buffer to erase a tab. + * + * Called by the eraser function, which knows how many character + * columns have been used since either a previous tab or the start + * of input. This information will be used later, along with + * canon column (if applicable), to go back the correct number + * of columns. + */ + +static void echo_erase_tab(unsigned int num_chars, int after_tab, + struct neuronspi_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_ERASE_TAB, ldata); + + /* We only need to know this modulo 8 (tab spacing) */ + num_chars &= 7; + + /* Set the high bit as a flag if num_chars is after a previous tab */ + if (after_tab) + num_chars |= 0x80; + + add_echo_byte(num_chars, ldata); +} + +/** + * echo_char_raw - echo a character raw + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * This variant does not treat control characters specially. + */ + +static void echo_char_raw(unsigned char c, struct neuronspi_tty_data *ldata) +{ + if (c == ECHO_OP_START) { + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); + } else { + add_echo_byte(c, ldata); + } +} + +/** + * echo_char - echo a character + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * This variant tags control characters to be echoed as "^X" + * (where X is the letter representing the control char). + */ + +static void echo_char(unsigned char c, struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (c == ECHO_OP_START) { + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); + } else { + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(c, ldata); + } +} + +/** + * finish_erasing - complete erase + * @ldata: n_tty data + */ + +static inline void finish_erasing(struct neuronspi_tty_data *ldata) +{ + if (ldata->erasing) { + echo_char_raw('/', ldata); + ldata->erasing = 0; + } +} + +/** + * eraser - handle erase function + * @c: character input + * @tty: terminal device + * + * Perform erase and necessary output when an erase character is + * present in the stream from the driver layer. Handles the complexities + * of UTF-8 multibyte symbols. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + */ + +static void eraser(unsigned char c, struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + enum { ERASE, WERASE, KILL } kill_type; + size_t head; + size_t cnt; + int seen_alnums; + + if (ldata->read_head == ldata->canon_head) { + /* process_output('\a', tty); */ /* what do you think? */ + return; + } + if (c == ERASE_CHAR(tty)) + kill_type = ERASE; + else if (c == WERASE_CHAR(tty)) + kill_type = WERASE; + else { + if (!L_ECHO(tty)) { + ldata->read_head = ldata->canon_head; + return; + } + if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) { + ldata->read_head = ldata->canon_head; + finish_erasing(ldata); + echo_char(KILL_CHAR(tty), tty); + /* Add a newline if ECHOK is on and ECHOKE is off. */ + if (L_ECHOK(tty)) + echo_char_raw('\n', ldata); + return; + } + kill_type = KILL; + } + + seen_alnums = 0; + while (ldata->read_head != ldata->canon_head) { + head = ldata->read_head; + + /* erase a single possibly multibyte character */ + do { + head--; + c = read_buf(ldata, head); + } while (is_continuation(c, tty) && head != ldata->canon_head); + + /* do not partially erase */ + if (is_continuation(c, tty)) + break; + + if (kill_type == WERASE) { + /* Equivalent to BSD's ALTWERASE. */ + if (isalnum(c) || c == '_') + seen_alnums++; + else if (seen_alnums) + break; + } + cnt = ldata->read_head - head; + ldata->read_head = head; + if (L_ECHO(tty)) { + if (L_ECHOPRT(tty)) { + if (!ldata->erasing) { + echo_char_raw('\\', ldata); + ldata->erasing = 1; + } + /* if cnt > 1, output a multi-byte character */ + echo_char(c, tty); + while (--cnt > 0) { + head++; + echo_char_raw(read_buf(ldata, head), ldata); + echo_move_back_col(ldata); + } + } else if (kill_type == ERASE && !L_ECHOE(tty)) { + echo_char(ERASE_CHAR(tty), tty); + } else if (c == '\t') { + unsigned int num_chars = 0; + int after_tab = 0; + size_t tail = ldata->read_head; + + /* + * Count the columns used for characters + * since the start of input or after a + * previous tab. + * This info is used to go back the correct + * number of columns. + */ + while (tail != ldata->canon_head) { + tail--; + c = read_buf(ldata, tail); + if (c == '\t') { + after_tab = 1; + break; + } else if (iscntrl(c)) { + if (L_ECHOCTL(tty)) + num_chars += 2; + } else if (!is_continuation(c, tty)) { + num_chars++; + } + } + echo_erase_tab(num_chars, after_tab, ldata); + } else { + if (iscntrl(c) && L_ECHOCTL(tty)) { + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); + } + if (!iscntrl(c) || L_ECHOCTL(tty)) { + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); + } + } + } + if (kill_type == ERASE) + break; + } + if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) + finish_erasing(ldata); +} + +/** + * isig - handle the ISIG optio + * @sig: signal + * @tty: terminal + * + * Called when a signal is being sent due to terminal input. + * Called from the driver receive_buf path so serialized. + * + * Performs input and output flush if !NOFLSH. In this context, the echo + * buffer is 'output'. The signal is processed first to alert any current + * readers or writers to discontinue and exit their i/o loops. + * + * Locking: ctrl_lock + */ + +static void __isig(int sig, struct tty_struct *tty) +{ + struct pid *tty_pgrp = tty_get_pgrp(tty); + if (tty_pgrp) { + kill_pgrp(tty_pgrp, sig, 1); + put_pid(tty_pgrp); + } +} + +static void isig(int sig, struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (L_NOFLSH(tty)) { + /* signal only */ + __isig(sig, tty); + + } else { /* signal and flush */ + up_read(&tty->termios_rwsem); + down_write(&tty->termios_rwsem); + + __isig(sig, tty); + + /* clear echo buffer */ + mutex_lock(&ldata->output_lock); + ldata->echo_head = ldata->echo_tail = 0; + ldata->echo_mark = ldata->echo_commit = 0; + mutex_unlock(&ldata->output_lock); + + /* clear output buffer */ + tty_driver_flush_buffer(tty); + + /* clear input buffer */ + reset_buffer_flags(tty->disc_data); + + /* notify pty master of flush */ + if (tty->link) + neuronspi_tty_packet_mode_flush(tty); + + up_write(&tty->termios_rwsem); + down_read(&tty->termios_rwsem); + } +} + +/** + * n_tty_receive_break - handle break + * @tty: terminal + * + * An RS232 break event has been hit in the incoming bitstream. This + * can cause a variety of events depending upon the termios settings. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * + * Note: may get exclusive termios_rwsem if flushing input buffer + */ + +static void neuronspi_tty_receive_break(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (I_IGNBRK(tty)) + return; + if (I_BRKINT(tty)) { + isig(SIGINT, tty); + return; + } + if (I_PARMRK(tty)) { + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); + } + put_tty_queue('\0', ldata); +} + +/** + * n_tty_receive_overrun - handle overrun reporting + * @tty: terminal + * + * Data arrived faster than we could process it. While the tty + * driver has flagged this the bits that were missed are gone + * forever. + * + * Called from the receive_buf path so single threaded. Does not + * need locking as num_overrun and overrun_time are function + * private. + */ + +static void neuronspi_tty_receive_overrun(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + ldata->num_overrun++; + if (time_after(jiffies, ldata->overrun_time + HZ) || + time_after(ldata->overrun_time, jiffies)) { + tty_warn(tty, "%d input overrun(s)\n", ldata->num_overrun); + ldata->overrun_time = jiffies; + ldata->num_overrun = 0; + } +} + +/** + * n_tty_receive_parity_error - error notifier + * @tty: terminal device + * @c: character + * + * Process a parity error and queue the right data to indicate + * the error case if necessary. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + */ +static void neuronspi_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (I_INPCK(tty)) { + if (I_IGNPAR(tty)) + return; + if (I_PARMRK(tty)) { + put_tty_queue('\377', ldata); + put_tty_queue('\0', ldata); + put_tty_queue(c, ldata); + } else + put_tty_queue('\0', ldata); + } else + put_tty_queue(c, ldata); +} + +static void +neuronspi_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) +{ + isig(signal, tty); + if (I_IXON(tty)) + start_tty(tty); + if (L_ECHO(tty)) { + echo_char(c, tty); + commit_echoes(tty); + } else + process_echoes(tty); + return; +} + +/** + * n_tty_receive_char - perform processing + * @tty: terminal device + * @c: character + * + * Process an individual character of input received from the driver. + * This is serialized with respect to itself by the rules for the + * driver above. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes canon_head if canonical mode is active + * + * Returns 1 if LNEXT was received, else returns 0 + */ + +static int +neuronspi_tty_receive_char_special(struct tty_struct *tty, unsigned char c) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (I_IXON(tty)) { + if (c == START_CHAR(tty)) { + start_tty(tty); + process_echoes(tty); + return 0; + } + if (c == STOP_CHAR(tty)) { + stop_tty(tty); + return 0; + } + } + + if (L_ISIG(tty)) { + if (c == INTR_CHAR(tty)) { + neuronspi_tty_receive_signal_char(tty, SIGINT, c); + return 0; + } else if (c == QUIT_CHAR(tty)) { + neuronspi_tty_receive_signal_char(tty, SIGQUIT, c); + return 0; + } else if (c == SUSP_CHAR(tty)) { + neuronspi_tty_receive_signal_char(tty, SIGTSTP, c); + return 0; + } + } + + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + + if (c == '\r') { + if (I_IGNCR(tty)) + return 0; + if (I_ICRNL(tty)) + c = '\n'; + } else if (c == '\n' && I_INLCR(tty)) + c = '\r'; + + if (ldata->icanon) { + if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || + (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) { + eraser(c, tty); + commit_echoes(tty); + return 0; + } + if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) { + ldata->lnext = 1; + if (L_ECHO(tty)) { + finish_erasing(ldata); + if (L_ECHOCTL(tty)) { + echo_char_raw('^', ldata); + echo_char_raw('\b', ldata); + commit_echoes(tty); + } + } + return 1; + } + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) { + size_t tail = ldata->canon_head; + + finish_erasing(ldata); + echo_char(c, tty); + echo_char_raw('\n', ldata); + while (tail != ldata->read_head) { + echo_char(read_buf(ldata, tail), tty); + tail++; + } + commit_echoes(tty); + return 0; + } + if (c == '\n') { + if (L_ECHO(tty) || L_ECHONL(tty)) { + echo_char_raw('\n', ldata); + commit_echoes(tty); + } + goto handle_newline; + } + if (c == EOF_CHAR(tty)) { + c = __DISABLED_CHAR; + goto handle_newline; + } + if ((c == EOL_CHAR(tty)) || + (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) { + /* + * XXX are EOL_CHAR and EOL2_CHAR echoed?!? + */ + if (L_ECHO(tty)) { + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + /* + * XXX does PARMRK doubling happen for + * EOL_CHAR and EOL2_CHAR? + */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + put_tty_queue(c, ldata); + +handle_newline: + set_bit(ldata->read_head & (N_TTY_BUF_SIZE - 1), ldata->read_flags); + put_tty_queue(c, ldata); + smp_store_release(&ldata->canon_head, ldata->read_head); + kill_fasync(&tty->fasync, SIGIO, POLL_IN); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); + return 0; + } + } + + if (L_ECHO(tty)) { + finish_erasing(ldata); + if (c == '\n') + echo_char_raw('\n', ldata); + else { + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + } + commit_echoes(tty); + } + + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + put_tty_queue(c, ldata); + + put_tty_queue(c, ldata); + return 0; +} + +static inline void +neuronspi_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + put_tty_queue(c, ldata); + put_tty_queue(c, ldata); +} + +static void neuronspi_tty_receive_char(struct tty_struct *tty, unsigned char c) +{ + neuronspi_tty_receive_char_inline(tty, c); +} + +static inline void +neuronspi_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty)) { + start_tty(tty); + process_echoes(tty); + } + if (L_ECHO(tty)) { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + echo_set_canon_col(ldata); + echo_char(c, tty); + commit_echoes(tty); + } + put_tty_queue(c, ldata); +} + +static void neuronspi_tty_receive_char_closing(struct tty_struct *tty, unsigned char c) +{ + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + + if (I_IXON(tty)) { + if (c == STOP_CHAR(tty)) + stop_tty(tty); + else if (c == START_CHAR(tty) || + (tty->stopped && !tty->flow_stopped && I_IXANY(tty) && + c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && + c != SUSP_CHAR(tty))) { + start_tty(tty); + process_echoes(tty); + } + } +} + +static void +neuronspi_tty_receive_char_flagged(struct tty_struct *tty, unsigned char c, char flag) +{ + switch (flag) { + case TTY_BREAK: + neuronspi_tty_receive_break(tty); + break; + case TTY_PARITY: + case TTY_FRAME: + neuronspi_tty_receive_parity_error(tty, c); + break; + case TTY_OVERRUN: + neuronspi_tty_receive_overrun(tty); + break; + default: + tty_err(tty, "unknown flag %d\n", flag); + break; + } +} + +static void +neuronspi_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + ldata->lnext = 0; + if (likely(flag == TTY_NORMAL)) { + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + neuronspi_tty_receive_char(tty, c); + } else + neuronspi_tty_receive_char_flagged(tty, c, flag); +} + +static void +neuronspi_tty_receive_buf_real_raw(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + size_t n, head; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = min_t(size_t, count, N_TTY_BUF_SIZE - head); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + cp += n; + count -= n; + + head = ldata->read_head & (N_TTY_BUF_SIZE - 1); + n = min_t(size_t, count, N_TTY_BUF_SIZE - head); + memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; +} + +static void neuronspi_tty_receive_buf_raw(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + put_tty_queue(*cp++, ldata); + else + neuronspi_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void neuronspi_tty_receive_buf_closing(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) + neuronspi_tty_receive_char_closing(tty, *cp++); + } +} + +static void neuronspi_tty_receive_buf_standard(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (I_ISTRIP(tty)) + c &= 0x7f; + if (I_IUCLC(tty) && L_IEXTEN(tty)) + c = tolower(c); + if (L_EXTPROC(tty)) { + put_tty_queue(c, ldata); + continue; + } + if (!test_bit(c, ldata->char_map)) + neuronspi_tty_receive_char_inline(tty, c); + else if (neuronspi_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + neuronspi_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } else + neuronspi_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void neuronspi_tty_receive_buf_fast(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) { + if (fp) + flag = *fp++; + if (likely(flag == TTY_NORMAL)) { + unsigned char c = *cp++; + + if (!test_bit(c, ldata->char_map)) + neuronspi_tty_receive_char_fast(tty, c); + else if (neuronspi_tty_receive_char_special(tty, c) && count) { + if (fp) + flag = *fp++; + neuronspi_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } else + neuronspi_tty_receive_char_flagged(tty, *cp++, flag); + } +} + +static void __receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + bool preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + + if (ldata->real_raw) + neuronspi_tty_receive_buf_real_raw(tty, cp, fp, count); + else if (ldata->raw || (L_EXTPROC(tty) && !preops)) + neuronspi_tty_receive_buf_raw(tty, cp, fp, count); + else if (tty->closing && !L_EXTPROC(tty)) + neuronspi_tty_receive_buf_closing(tty, cp, fp, count); + else { + if (ldata->lnext) { + char flag = TTY_NORMAL; + + if (fp) + flag = *fp++; + neuronspi_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + + if (!preops && !I_PARMRK(tty)) + neuronspi_tty_receive_buf_fast(tty, cp, fp, count); + else + neuronspi_tty_receive_buf_standard(tty, cp, fp, count); + + flush_echoes(tty); + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); + } + + if (ldata->icanon && !L_EXTPROC(tty)) + return; + + /* publish read_head to consumer */ + smp_store_release(&ldata->commit_head, ldata->read_head); + + if (read_cnt(ldata)) { + kill_fasync(&tty->fasync, SIGIO, POLL_IN); + wake_up_interruptible_poll(&tty->read_wait, POLLIN); + } +} + + +static int neuronspi_tty_open(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata; + + /* Currently a malloc failure here can panic */ + ldata = vmalloc(sizeof(*ldata)); + if (!ldata) + goto err; + + ldata->overrun_time = jiffies; + mutex_init(&ldata->atomic_read_lock); + mutex_init(&ldata->output_lock); + + tty->disc_data = ldata; + reset_buffer_flags(tty->disc_data); + ldata->column = 0; + ldata->canon_column = 0; + ldata->num_overrun = 0; + ldata->no_room = 0; + ldata->lnext = 0; + tty->closing = 0; + /* indicate buffer work may resume */ + clear_bit(TTY_LDISC_HALTED, &tty->flags); + //n_tty_set_termios(tty, NULL); + tty_unthrottle(tty); + + return 0; +err: + return -ENOMEM; +} + +static void neuronspi_tty_close(struct tty_struct *tty) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + //if (tty->link) + //n_tty_packet_mode_flush(tty); + + vfree(ldata); + tty->disc_data = NULL; +} + +static inline int input_available_p(struct tty_struct *tty, int poll) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1; + + if (ldata->icanon && !L_EXTPROC(tty)) + return ldata->canon_head != ldata->read_tail; + else + return ldata->commit_head - ldata->read_tail >= amt; +} + +/** + * copy_from_read_buf - copy read data directly + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function to speed up n_tty_read. It is only called when + * ICANON is off; it copies characters straight from the tty queue to + * user space directly. It can be profitably called twice; once to + * drain the space from the tail pointer to the (physical) end of the + * buffer, and once to drain the space from the (physical) beginning of + * the buffer to head pointer. + * + * Called under the ldata->atomic_read_lock sem + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int copy_from_read_buf(struct tty_struct *tty, + unsigned char __user **b, + size_t *nr) + +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int retval; + size_t n; + bool is_eof; + size_t head = smp_load_acquire(&ldata->commit_head); + size_t tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); + + retval = 0; + n = min(head - ldata->read_tail, N_TTY_BUF_SIZE - tail); + n = min(*nr, n); + if (n) { + const unsigned char *from = read_buf_addr(ldata, tail); + retval = copy_to_user(*b, from, n); + n -= retval; + is_eof = n == 1 && *from == EOF_CHAR(tty); + printk(KERN_INFO "NEURONSPI: ReadBufChar %x", *from); + tty_audit_add_data(tty, from, n); + smp_store_release(&ldata->read_tail, ldata->read_tail + n); + /* Turn single EOF into zero-length read */ + if (L_EXTPROC(tty) && ldata->icanon && is_eof && + (head == ldata->read_tail)) + n = 0; + *b += n; + *nr -= n; + } + return retval; +} + +/** + * canon_copy_from_read_buf - copy read data in canonical mode + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function for n_tty_read. It is only called when ICANON is on; + * it copies one line of input up to and including the line-delimiting + * character into the user-space buffer. + * + * NB: When termios is changed from non-canonical to canonical mode and + * the read buffer contains data, n_tty_set_termios() simulates an EOF + * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer. + * This causes data already processed as input to be immediately available + * as input although a newline has not been received. + * + * Called under the atomic_read_lock mutex + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int canon_copy_from_read_buf(struct tty_struct *tty, + unsigned char __user **b, + size_t *nr) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + size_t n, size, more, c; + size_t eol; + size_t tail; + int ret, found = 0; + + /* N.B. avoid overrun if nr == 0 */ + if (!*nr) + return 0; + + n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); + + tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); + size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); + + n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n", + __func__, *nr, tail, n, size); + + eol = find_next_bit(ldata->read_flags, size, tail); + more = n - (size - tail); + if (eol == N_TTY_BUF_SIZE && more) { + /* scan wrapped without finding set bit */ + eol = find_next_bit(ldata->read_flags, more, 0); + found = eol != more; + } else + found = eol != size; + + n = eol - tail; + if (n > N_TTY_BUF_SIZE) + n += N_TTY_BUF_SIZE; + c = n + found; + + if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) { + c = min(*nr, c); + n = c; + } + + n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n", + __func__, eol, found, n, c, tail, more); + + ret = tty_copy_to_user(tty, *b, tail, n); + if (ret) + return -EFAULT; + *b += n; + *nr -= n; + + if (found) + clear_bit(eol, ldata->read_flags); + smp_store_release(&ldata->read_tail, ldata->read_tail + c); + + if (found) { + if (!ldata->push) + ldata->line_start = ldata->read_tail; + else + ldata->push = 0; + tty_audit_push(); + } + return 0; +} + +extern ssize_t redirected_tty_write(struct file *, const char __user *, + size_t, loff_t *); + +/** + * job_control - check job control + * @tty: tty + * @file: file handle + * + * Perform job control management checks on this file/tty descriptor + * and if appropriate send any needed signals and return a negative + * error code if action should be taken. + * + * Locking: redirected write test is safe + * current->signal->tty check is safe + * ctrl_lock to safely reference tty->pgrp + */ + +static int job_control(struct tty_struct *tty, struct file *file) +{ + /* Job control check -- must be done at start and after + every sleep (POSIX.1 7.1.1.4). */ + /* NOTE: not yet done after every sleep pending a thorough + check of the logic of this change. -- jlc */ + /* don't stop on /dev/console */ + if (file->f_op->write == redirected_tty_write) + return 0; + + return __tty_check_change(tty, SIGTTIN); +} + +int neuronspi_tty_ioctl(struct tty_struct *tty_s, struct file *tty_f, unsigned int cmd, unsigned long arg) +{ + int err = 0; + printk(KERN_INFO "NEURONSPI: TTY IOCTL %d %ld", cmd, arg); + switch (cmd) { + case 0x5480: { + printk(KERN_INFO "NEURONSPI: m_Params.FrameSpaceChars (0x5480) %d\n", (u32)arg); + err = (s32)arg; + break; + } + case 0x5481: { + printk(KERN_INFO "NEURONSPI: m_Params.FrameSpaceTimeInUs (0x5481) %d\n", (u32)arg); + err = (s32)arg; + break; + } + default: { + err = n_tty_ioctl_helper(tty_s, tty_f, cmd, arg); + break; + } + } + return err; +} + +static int +neuronspi_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count, int flow) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + int room, n, rcvd = 0, overflow; + + down_read(&tty->termios_rwsem); + + while (1) { + /* + * When PARMRK is set, each input char may take up to 3 chars + * in the read buf; reduce the buffer space avail by 3x + * + * If we are doing input canonicalization, and there are no + * pending newlines, let characters through without limit, so + * that erase characters will be handled. Other excess + * characters will be beeped. + * + * paired with store in *_copy_from_read_buf() -- guarantees + * the consumer has loaded the data in read_buf up to the new + * read_tail (so this producer will not overwrite unread data) + */ + size_t tail = smp_load_acquire(&ldata->read_tail); + + room = N_TTY_BUF_SIZE - (ldata->read_head - tail); + if (I_PARMRK(tty)) + room = (room + 2) / 3; + room--; + if (room <= 0) { + overflow = ldata->icanon && ldata->canon_head == tail; + if (overflow && room < 0) + ldata->read_head--; + room = overflow; + ldata->no_room = flow && !room; + } else + overflow = 0; + + n = min(count, room); + if (!n) + break; + + /* ignore parity errors if handling overflow */ + if (!overflow || !fp || *fp != TTY_PARITY) + __receive_buf(tty, cp, fp, n); + cp += n; + if (fp) + fp += n; + count -= n; + rcvd += n; + } + + tty->receive_room = room; + + /* Unthrottle if handling overflow on pty */ + if (tty->driver->type == TTY_DRIVER_TYPE_PTY) { + if (overflow) { + tty_set_flow_change(tty, TTY_UNTHROTTLE_SAFE); + tty_unthrottle_safe(tty); + __tty_set_flow_change(tty, 0); + } + } else + neuronspi_tty_check_throttle(tty); + + up_read(&tty->termios_rwsem); + + return rcvd; +} + +static void neuronspi_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + neuronspi_tty_receive_buf_common(tty, cp, fp, count, 0); + printk(KERN_INFO "NEURONSPI: Receive Buffer 1 called, count %d", count); +} + +static int neuronspi_tty_receive_buf2(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + printk(KERN_INFO "NEURONSPI: Receive Buffer 2 called, count %d", count); + return neuronspi_tty_receive_buf_common(tty, cp, fp, count, 1); +} + +/** + * n_tty_read - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + * + * n_tty_read()/consumer path: + * claims non-exclusive termios_rwsem + * publishes read_tail + */ + +static ssize_t neuronspi_tty_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + unsigned char __user *b = buf; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int c; + int minimum, time; + ssize_t retval = 0; + long timeout; + int packet; + size_t tail; + + c = job_control(tty, file); + printk(KERN_INFO "TTYRead Called"); + if (c < 0) + return c; + + printk(KERN_INFO "TTYRead %d", (uint32_t)nr); + /* + * Internal serialization of reads. + */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&ldata->atomic_read_lock)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&ldata->atomic_read_lock)) + return -ERESTARTSYS; + } + + down_read(&tty->termios_rwsem); + + minimum = time = 0; + timeout = MAX_SCHEDULE_TIMEOUT; + if (!ldata->icanon) { + minimum = MIN_CHAR(tty); + if (minimum) { + time = (HZ / 10) * TIME_CHAR(tty); + } else { + timeout = (HZ / 10) * TIME_CHAR(tty); + minimum = 1; + } + } + + packet = tty->packet; + tail = ldata->read_tail; + + add_wait_queue(&tty->read_wait, &wait); + while (nr) { + /* First test for status change. */ + if (packet && tty->link->ctrl_status) { + unsigned char cs; + if (b != buf) + break; + spin_lock_irq(&tty->link->ctrl_lock); + cs = tty->link->ctrl_status; + tty->link->ctrl_status = 0; + spin_unlock_irq(&tty->link->ctrl_lock); + if (put_user(cs, b)) { + retval = -EFAULT; + break; + } + b++; + nr--; + break; + } + + if (!input_available_p(tty, 0)) { + up_read(&tty->termios_rwsem); + tty_buffer_flush_work(tty->port); + down_read(&tty->termios_rwsem); + if (!input_available_p(tty, 0)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + retval = -EIO; + break; + } + if (tty_hung_up_p(file)) + break; + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + up_read(&tty->termios_rwsem); + + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); + + down_read(&tty->termios_rwsem); + continue; + } + } + + if (ldata->icanon && !L_EXTPROC(tty)) { + retval = canon_copy_from_read_buf(tty, &b, &nr); + if (retval) + break; + } else { + int uncopied; + + /* Deal with packet mode. */ + if (packet && b == buf) { + if (put_user(TIOCPKT_DATA, b)) { + retval = -EFAULT; + break; + } + b++; + nr--; + } + + uncopied = copy_from_read_buf(tty, &b, &nr); + uncopied += copy_from_read_buf(tty, &b, &nr); + if (uncopied) { + retval = -EFAULT; + break; + } + } + + neuronspi_tty_check_unthrottle(tty); + + if (b - buf >= minimum) + break; + if (time) + timeout = time; + } + if (tail != ldata->read_tail) + neuronspi_tty_kick_worker(tty); + up_read(&tty->termios_rwsem); + + remove_wait_queue(&tty->read_wait, &wait); + mutex_unlock(&ldata->atomic_read_lock); + + if (b - buf) + retval = b - buf; + + return retval; +} + +/** + * n_tty_write - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Write function of the terminal device. This is serialized with + * respect to other write callers but not to termios changes, reads + * and other such events. Since the receive code will echo characters, + * thus calling driver write methods, the output_lock is used in + * the output processing functions called here as well as in the + * echo processing function to protect the column state and space + * left in the buffer. + * + * This code must be sure never to sleep through a hangup. + * + * Locking: output_lock to protect column state and space left + * (note that the process_output*() functions take this + * lock themselves) + */ + +static ssize_t neuronspi_tty_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + const unsigned char *b = buf; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int c; + ssize_t retval = 0; + + /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */ + if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) { + retval = tty_check_change(tty); + if (retval) + return retval; + } + + down_read(&tty->termios_rwsem); + + /* Write out any echoed characters that are still pending */ + process_echoes(tty); + + add_wait_queue(&tty->write_wait, &wait); + while (1) { + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) { + retval = -EIO; + break; + } + if (O_OPOST(tty)) { + while (nr > 0) { + ssize_t num = process_output_block(tty, b, nr); + if (num < 0) { + if (num == -EAGAIN) + break; + retval = num; + goto break_out; + } + b += num; + nr -= num; + if (nr == 0) + break; + c = *b; + if (process_output(c, tty) < 0) + break; + b++; nr--; + } + if (tty->ops->flush_chars) + tty->ops->flush_chars(tty); + } else { + struct neuronspi_tty_data *ldata = tty->disc_data; + + while (nr > 0) { + mutex_lock(&ldata->output_lock); + c = tty->ops->write(tty, b, nr); + mutex_unlock(&ldata->output_lock); + if (c < 0) { + retval = c; + goto break_out; + } + if (!c) + break; + b += c; + nr -= c; + } + } + if (!nr) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + up_read(&tty->termios_rwsem); + + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + + down_read(&tty->termios_rwsem); + } +break_out: + remove_wait_queue(&tty->write_wait, &wait); + if (nr && tty->fasync) + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + up_read(&tty->termios_rwsem); + return (b - buf) ? b - buf : retval; +} + +/** + * n_tty_poll - poll method for N_TTY + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + */ + +static unsigned int neuronspi_tty_poll(struct tty_struct *tty, struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + printk(KERN_INFO "TTYPoll Called"); + poll_wait(file, &tty->read_wait, wait); + poll_wait(file, &tty->write_wait, wait); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + else { + tty_buffer_flush_work(tty->port); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + } + if (tty->packet && tty->link->ctrl_status) + mask |= POLLPRI | POLLIN | POLLRDNORM; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + mask |= POLLHUP; + if (tty_hung_up_p(file)) + mask |= POLLHUP; + if (tty->ops->write && !tty_is_writelocked(tty) && + tty_chars_in_buffer(tty) < WAKEUP_CHARS && + tty_write_room(tty) > 0) + mask |= POLLOUT | POLLWRNORM; + return mask; +} + +static void neuronspi_tty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + struct neuronspi_tty_data *ldata = tty->disc_data; + + if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) { + bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE); + ldata->line_start = ldata->read_tail; + if (!L_ICANON(tty) || !read_cnt(ldata)) { + ldata->canon_head = ldata->read_tail; + ldata->push = 0; + } else { + set_bit((ldata->read_head - 1) & (N_TTY_BUF_SIZE - 1), + ldata->read_flags); + ldata->canon_head = ldata->read_head; + ldata->push = 1; + } + ldata->commit_head = ldata->read_head; + ldata->erasing = 0; + ldata->lnext = 0; + } + + ldata->icanon = (L_ICANON(tty) != 0); + + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || + I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || + I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || + I_PARMRK(tty)) { + bitmap_zero(ldata->char_map, 256); + + if (I_IGNCR(tty) || I_ICRNL(tty)) + set_bit('\r', ldata->char_map); + if (I_INLCR(tty)) + set_bit('\n', ldata->char_map); + + if (L_ICANON(tty)) { + set_bit(ERASE_CHAR(tty), ldata->char_map); + set_bit(KILL_CHAR(tty), ldata->char_map); + set_bit(EOF_CHAR(tty), ldata->char_map); + set_bit('\n', ldata->char_map); + set_bit(EOL_CHAR(tty), ldata->char_map); + if (L_IEXTEN(tty)) { + set_bit(WERASE_CHAR(tty), ldata->char_map); + set_bit(LNEXT_CHAR(tty), ldata->char_map); + set_bit(EOL2_CHAR(tty), ldata->char_map); + if (L_ECHO(tty)) + set_bit(REPRINT_CHAR(tty), + ldata->char_map); + } + } + if (I_IXON(tty)) { + set_bit(START_CHAR(tty), ldata->char_map); + set_bit(STOP_CHAR(tty), ldata->char_map); + } + if (L_ISIG(tty)) { + set_bit(INTR_CHAR(tty), ldata->char_map); + set_bit(QUIT_CHAR(tty), ldata->char_map); + set_bit(SUSP_CHAR(tty), ldata->char_map); + } + clear_bit(__DISABLED_CHAR, ldata->char_map); + ldata->raw = 0; + ldata->real_raw = 0; + } else { + ldata->raw = 1; + if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && + (I_IGNPAR(tty) || !I_INPCK(tty)) && + (tty->driver->flags & TTY_DRIVER_REAL_RAW)) + ldata->real_raw = 1; + else + ldata->real_raw = 0; + } + /* + * Fix tty hang when I_IXON(tty) is cleared, but the tty + * been stopped by STOP_CHAR(tty) before it. + */ + if (!I_IXON(tty) && old && (old->c_iflag & IXON) && !tty->flow_stopped) { + start_tty(tty); + process_echoes(tty); + } + + /* The termios change make the tty ready for I/O */ + wake_up_interruptible(&tty->write_wait); + wake_up_interruptible(&tty->read_wait); +} +int neuronspi_tty_init() +{ + int err; + static struct tty_ldisc_ops neuronspi_tty_ldisc; + printk(KERN_INFO "TTYInit\n"); + memset(&neuronspi_tty_ldisc, 0, sizeof(neuronspi_tty_ldisc)); + neuronspi_tty_ldisc.magic = TTY_LDISC_MAGIC; + neuronspi_tty_ldisc.name = "unipi_tty"; + neuronspi_tty_ldisc.ioctl = neuronspi_tty_ioctl; + neuronspi_tty_ldisc.owner = THIS_MODULE; + neuronspi_tty_ldisc.open = neuronspi_tty_open; + neuronspi_tty_ldisc.close = neuronspi_tty_close; + neuronspi_tty_ldisc.flush_buffer = neuronspi_tty_flush_buffer, + neuronspi_tty_ldisc.read = neuronspi_tty_read, + neuronspi_tty_ldisc.write = neuronspi_tty_write, + neuronspi_tty_ldisc.ioctl = neuronspi_tty_ioctl, + neuronspi_tty_ldisc.set_termios = neuronspi_tty_set_termios, + neuronspi_tty_ldisc.poll = neuronspi_tty_poll, + neuronspi_tty_ldisc.receive_buf = neuronspi_tty_receive_buf, + neuronspi_tty_ldisc.write_wakeup = neuronspi_tty_write_wakeup, + neuronspi_tty_ldisc.receive_buf2 = neuronspi_tty_receive_buf2, + err = tty_register_ldisc(N_PROFIBUS_FDL, &neuronspi_tty_ldisc); + if (err) { + printk(KERN_INFO "UniPi line discipline registration failed. (%d)", err); + return err; + } + return 0; +} diff --git a/modules/unipi/src/unipi_tty.h b/modules/unipi/src/unipi_tty.h index d9a11ad..13c7664 100644 --- a/modules/unipi/src/unipi_tty.h +++ b/modules/unipi/src/unipi_tty.h @@ -17,6 +17,6 @@ #include "unipi_common.h" - +int neuronspi_tty_init(void); #endif /* MODULES_UNIPI_SRC_UNIPI_TTY_H_ */ diff --git a/modules/unipi/src/unipi_uart.c b/modules/unipi/src/unipi_uart.c index 768afbe..7007e1b 100644 --- a/modules/unipi/src/unipi_uart.c +++ b/modules/unipi/src/unipi_uart.c @@ -31,6 +31,44 @@ struct uart_driver* neuronspi_uart; * Non-static Functions * ************************/ +static void neuronspi_uart_set_iflags(struct uart_port *port, int to) +{ + u8 *inp_buf, *outp_buf; + int write_length; + struct neuronspi_port *n_port; + struct spi_device *spi; + struct neuronspi_driver_data *n_spi; + n_port = to_neuronspi_port(port, port); + spi = neuronspi_s_dev[n_port->dev_index]; + n_spi = spi_get_drvdata(spi); +#if NEURONSPI_DETAILED_DEBUG > 0 + printk(KERN_INFO "NEURONSPI: SET PARMRK to %d\n", to); +#endif + write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_IFLAGS_REGISTER, &inp_buf, &outp_buf, to); + neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); + kfree(inp_buf); + kfree(outp_buf); +} + +void neuronspi_uart_set_ldisc(struct uart_port *port, struct ktermios *kterm) +{ + u8 *inp_buf, *outp_buf; + int write_length; + struct neuronspi_port *n_port; + struct spi_device *spi; + struct neuronspi_driver_data *n_spi; + n_port = to_neuronspi_port(port, port); + spi = neuronspi_s_dev[n_port->dev_index]; + n_spi = spi_get_drvdata(spi); +#if NEURONSPI_DETAILED_DEBUG > 0 + printk(KERN_INFO "NEURONSPI: PROFIBUS discipline set\n"); +#endif + write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_LDISC_REGISTER, &inp_buf, &outp_buf, kterm->c_line); + neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); + kfree(inp_buf); + kfree(outp_buf); +} + void neuronspi_uart_tx_proc(struct kthread_work *ws) { struct neuronspi_port *port = to_neuronspi_port(ws, tx_work); @@ -81,7 +119,7 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig printk(KERN_INFO "NEURONSPI: IOCTL 0x5480\n"); #endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, (ioctl_arg * 1000000) / n_port->baud); - neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1); + neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); return 1; @@ -91,7 +129,7 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig printk(KERN_INFO "NEURONSPI: IOCTL 0x5481\n"); #endif write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_TIMEOUT_REGISTER, &inp_buf, &outp_buf, ioctl_arg); - neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1); + neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1, 0); kfree(inp_buf); kfree(outp_buf); return 1; @@ -102,43 +140,7 @@ int neuronspi_uart_ioctl (struct uart_port *port, unsigned int ioctl_code, unsig } } -void neuronspi_uart_set_parmrk(struct uart_port *port, int to) -{ - u8 *inp_buf, *outp_buf; - int write_length; - struct neuronspi_port *n_port; - struct spi_device *spi; - struct neuronspi_driver_data *n_spi; - n_port = to_neuronspi_port(port, port); - spi = neuronspi_s_dev[n_port->dev_index]; - n_spi = spi_get_drvdata(spi); -#if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "NEURONSPI: SET PARMRK to %d\n", to); -#endif - write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_IFLAGS_REGISTER, &inp_buf, &outp_buf, to); - neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1); - kfree(inp_buf); - kfree(outp_buf); -} -void neuronspi_uart_set_ldisc(struct uart_port *port, struct ktermios *kterm) -{ - u8 *inp_buf, *outp_buf; - int write_length; - struct neuronspi_port *n_port; - struct spi_device *spi; - struct neuronspi_driver_data *n_spi; - n_port = to_neuronspi_port(port, port); - spi = neuronspi_s_dev[n_port->dev_index]; - n_spi = spi_get_drvdata(spi); -#if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_INFO "NEURONSPI: PROFIBUS discipline set\n"); -#endif - write_length = neuronspi_spi_compose_single_register_write(NEURONSPI_UART_LDISC_REGISTER, &inp_buf, &outp_buf, kterm->c_line); - neuronspi_spi_send_message(spi, inp_buf, outp_buf, write_length, n_spi->ideal_frequency, 25, 1); - kfree(inp_buf); - kfree(outp_buf); -} void neuronspi_uart_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { @@ -155,12 +157,13 @@ void neuronspi_uart_set_termios(struct uart_port *port, struct ktermios *termios neuronspi_spi_uart_set_cflag(neuronspi_s_dev[n_port->dev_index], n_port->dev_port, termios->c_cflag); if (old && termios && (old->c_iflag & PARMRK) != (termios->c_iflag & PARMRK)) { if (termios->c_iflag & PARMRK) { - neuronspi_uart_set_parmrk(port, 1); + neuronspi_uart_set_iflags(port, termios->c_iflag); } else { - neuronspi_uart_set_parmrk(port, 0); + neuronspi_uart_set_iflags(port, termios->c_iflag); } } if (old && termios && old->c_line != termios->c_line) { + printk(KERN_INFO "NEURONSPI: Line Discipline change/n"); if (termios->c_line == N_PROFIBUS_FDL) { #if NEURONSPI_DETAILED_DEBUG > 0 printk(KERN_INFO "NEURONSPI: Line Discipline change/n"); @@ -186,29 +189,35 @@ const char* neuronspi_uart_type(struct uart_port *port) s32 neuronspi_uart_request_port(struct uart_port *port) { #if NEURONSPI_DETAILED_DEBUG > 0 - printk(KERN_DEBUG "NEURONSPI: Requested port %d\n", port->line); + printk(KERN_DEBUG "NEURONSPI: UART requested port %d\n", port->line); #endif return 0; } void neuronspi_uart_fifo_read(struct uart_port *port, u32 rxlen) { + s32 i; struct neuronspi_port *s = to_neuronspi_port(port,port); struct neuronspi_driver_data *d_data = spi_get_drvdata(neuronspi_s_dev[s->dev_index]); -#if NEURONSPI_DETAILED_DEBUG > 0 +#if NEURONSPI_DETAILED_DEBUG > 2 printk(KERN_INFO "NEURONSPI: FIFO Read len:%d\n", rxlen); #endif memcpy(s->buf, d_data->uart_buf, rxlen); + for (i = 0; i < rxlen; i++) { +#if NEURONSPI_DETAILED_DEBUG > 2 + printk(KERN_INFO "NEURONSPI: UART Char Read: %x\n", d_data->uart_buf[i]); +#endif + } } void neuronspi_uart_fifo_write(struct neuronspi_port *port, u8 to_send) { s32 i; -#if NEURONSPI_DETAILED_DEBUG > 0 +#if NEURONSPI_DETAILED_DEBUG > 2 printk(KERN_INFO "NEURONSPI: FIFO Write to_send:%d\n", to_send); #endif for (i = 0; i < to_send; i++) { -#if NEURONSPI_DETAILED_DEBUG > 0 +#if NEURONSPI_DETAILED_DEBUG > 2 printk(KERN_INFO "NEURONSPI: UART Char Send: %x\n", port->buf[i]); #endif } @@ -307,7 +316,7 @@ void neuronspi_uart_handle_irq(struct neuronspi_uart_data *uart_data, u32 portno u8 *send_buf = kzalloc(NEURONSPI_UART_PROBE_MESSAGE_LEN, GFP_KERNEL); u8 *recv_buf = kzalloc(NEURONSPI_UART_PROBE_MESSAGE_LEN, GFP_KERNEL); memcpy(send_buf, NEURONSPI_UART_PROBE_MESSAGE, NEURONSPI_UART_PROBE_MESSAGE_LEN); - neuronspi_spi_send_message(spi, send_buf, recv_buf, NEURONSPI_UART_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1); + neuronspi_spi_send_message(spi, send_buf, recv_buf, NEURONSPI_UART_PROBE_MESSAGE_LEN, NEURONSPI_DEFAULT_FREQ, 25, 1, 0); kfree(send_buf); kfree(recv_buf); } @@ -410,7 +419,7 @@ s32 neuronspi_uart_probe(struct spi_device* dev, u8 device_index) uart_data->p[j].port.fifosize = NEURONSPI_FIFO_SIZE; uart_data->p[j].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY; uart_data->p[j].port.iotype = UPIO_PORT; - uart_data->p[j].port.uartclk = 9800; + uart_data->p[j].port.uartclk = 9600; uart_data->p[j].port.rs485_config = neuronspi_uart_config_rs485; uart_data->p[j].port.ops = &neuronspi_uart_ops; uart_data->p[j].port.line = neuronspi_uart_alloc_line();