18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-1.0+ 28c2ecf20Sopenharmony_ci/* r3964 linediscipline for linux 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * ----------------------------------------------------------- 58c2ecf20Sopenharmony_ci * Copyright by 68c2ecf20Sopenharmony_ci * Philips Automation Projects 78c2ecf20Sopenharmony_ci * Kassel (Germany) 88c2ecf20Sopenharmony_ci * ----------------------------------------------------------- 98c2ecf20Sopenharmony_ci * Author: 108c2ecf20Sopenharmony_ci * L. Haag 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * $Log: n_r3964.c,v $ 138c2ecf20Sopenharmony_ci * Revision 1.10 2001/03/18 13:02:24 dwmw2 148c2ecf20Sopenharmony_ci * Fix timer usage, use spinlocks properly. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Revision 1.9 2001/03/18 12:52:14 dwmw2 178c2ecf20Sopenharmony_ci * Merge changes in 2.4.2 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Revision 1.8 2000/03/23 14:14:54 dwmw2 208c2ecf20Sopenharmony_ci * Fix race in sleeping in r3964_read() 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Revision 1.7 1999/28/08 11:41:50 dwmw2 238c2ecf20Sopenharmony_ci * Port to 2.3 kernel 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * Revision 1.6 1998/09/30 00:40:40 dwmw2 268c2ecf20Sopenharmony_ci * Fixed compilation on 2.0.x kernels 278c2ecf20Sopenharmony_ci * Updated to newly registered tty-ldisc number 9 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Revision 1.5 1998/09/04 21:57:36 dwmw2 308c2ecf20Sopenharmony_ci * Signal handling bug fixes, port to 2.1.x. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Revision 1.4 1998/04/02 20:26:59 lhaag 338c2ecf20Sopenharmony_ci * select, blocking, ... 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Revision 1.3 1998/02/12 18:58:43 root 368c2ecf20Sopenharmony_ci * fixed some memory leaks 378c2ecf20Sopenharmony_ci * calculation of checksum characters 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * Revision 1.2 1998/02/07 13:03:34 root 408c2ecf20Sopenharmony_ci * ioctl read_telegram 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * Revision 1.1 1998/02/06 19:21:03 root 438c2ecf20Sopenharmony_ci * Initial revision 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/module.h> 498c2ecf20Sopenharmony_ci#include <linux/kernel.h> 508c2ecf20Sopenharmony_ci#include <linux/sched.h> 518c2ecf20Sopenharmony_ci#include <linux/types.h> 528c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 538c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 548c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 558c2ecf20Sopenharmony_ci#include <linux/ioport.h> 568c2ecf20Sopenharmony_ci#include <linux/in.h> 578c2ecf20Sopenharmony_ci#include <linux/slab.h> 588c2ecf20Sopenharmony_ci#include <linux/tty.h> 598c2ecf20Sopenharmony_ci#include <linux/errno.h> 608c2ecf20Sopenharmony_ci#include <linux/string.h> /* used in new tty drivers */ 618c2ecf20Sopenharmony_ci#include <linux/signal.h> /* used in new tty drivers */ 628c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 638c2ecf20Sopenharmony_ci#include <linux/n_r3964.h> 648c2ecf20Sopenharmony_ci#include <linux/poll.h> 658c2ecf20Sopenharmony_ci#include <linux/init.h> 668c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/*#define DEBUG_QUEUE*/ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Log successful handshake and protocol operations */ 718c2ecf20Sopenharmony_ci/*#define DEBUG_PROTO_S*/ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Log handshake and protocol errors: */ 748c2ecf20Sopenharmony_ci/*#define DEBUG_PROTO_E*/ 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* Log Linediscipline operations (open, close, read, write...): */ 778c2ecf20Sopenharmony_ci/*#define DEBUG_LDISC*/ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Log module and memory operations (init, cleanup; kmalloc, kfree): */ 808c2ecf20Sopenharmony_ci/*#define DEBUG_MODUL*/ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Macro helpers for debug output: */ 838c2ecf20Sopenharmony_ci#define TRACE(format, args...) printk("r3964: " format "\n" , ## args) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#ifdef DEBUG_MODUL 868c2ecf20Sopenharmony_ci#define TRACE_M(format, args...) printk("r3964: " format "\n" , ## args) 878c2ecf20Sopenharmony_ci#else 888c2ecf20Sopenharmony_ci#define TRACE_M(fmt, arg...) do {} while (0) 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci#ifdef DEBUG_PROTO_S 918c2ecf20Sopenharmony_ci#define TRACE_PS(format, args...) printk("r3964: " format "\n" , ## args) 928c2ecf20Sopenharmony_ci#else 938c2ecf20Sopenharmony_ci#define TRACE_PS(fmt, arg...) do {} while (0) 948c2ecf20Sopenharmony_ci#endif 958c2ecf20Sopenharmony_ci#ifdef DEBUG_PROTO_E 968c2ecf20Sopenharmony_ci#define TRACE_PE(format, args...) printk("r3964: " format "\n" , ## args) 978c2ecf20Sopenharmony_ci#else 988c2ecf20Sopenharmony_ci#define TRACE_PE(fmt, arg...) do {} while (0) 998c2ecf20Sopenharmony_ci#endif 1008c2ecf20Sopenharmony_ci#ifdef DEBUG_LDISC 1018c2ecf20Sopenharmony_ci#define TRACE_L(format, args...) printk("r3964: " format "\n" , ## args) 1028c2ecf20Sopenharmony_ci#else 1038c2ecf20Sopenharmony_ci#define TRACE_L(fmt, arg...) do {} while (0) 1048c2ecf20Sopenharmony_ci#endif 1058c2ecf20Sopenharmony_ci#ifdef DEBUG_QUEUE 1068c2ecf20Sopenharmony_ci#define TRACE_Q(format, args...) printk("r3964: " format "\n" , ## args) 1078c2ecf20Sopenharmony_ci#else 1088c2ecf20Sopenharmony_ci#define TRACE_Q(fmt, arg...) do {} while (0) 1098c2ecf20Sopenharmony_ci#endif 1108c2ecf20Sopenharmony_cistatic void add_tx_queue(struct r3964_info *, struct r3964_block_header *); 1118c2ecf20Sopenharmony_cistatic void remove_from_tx_queue(struct r3964_info *pInfo, int error_code); 1128c2ecf20Sopenharmony_cistatic void put_char(struct r3964_info *pInfo, unsigned char ch); 1138c2ecf20Sopenharmony_cistatic void trigger_transmit(struct r3964_info *pInfo); 1148c2ecf20Sopenharmony_cistatic void retry_transmit(struct r3964_info *pInfo); 1158c2ecf20Sopenharmony_cistatic void transmit_block(struct r3964_info *pInfo); 1168c2ecf20Sopenharmony_cistatic void receive_char(struct r3964_info *pInfo, const unsigned char c); 1178c2ecf20Sopenharmony_cistatic void receive_error(struct r3964_info *pInfo, const char flag); 1188c2ecf20Sopenharmony_cistatic void on_timeout(struct timer_list *t); 1198c2ecf20Sopenharmony_cistatic int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg); 1208c2ecf20Sopenharmony_cistatic int read_telegram(struct r3964_info *pInfo, struct pid *pid, 1218c2ecf20Sopenharmony_ci unsigned char __user * buf); 1228c2ecf20Sopenharmony_cistatic void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, 1238c2ecf20Sopenharmony_ci int error_code, struct r3964_block_header *pBlock); 1248c2ecf20Sopenharmony_cistatic struct r3964_message *remove_msg(struct r3964_info *pInfo, 1258c2ecf20Sopenharmony_ci struct r3964_client_info *pClient); 1268c2ecf20Sopenharmony_cistatic void remove_client_block(struct r3964_info *pInfo, 1278c2ecf20Sopenharmony_ci struct r3964_client_info *pClient); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int r3964_open(struct tty_struct *tty); 1308c2ecf20Sopenharmony_cistatic void r3964_close(struct tty_struct *tty); 1318c2ecf20Sopenharmony_cistatic ssize_t r3964_read(struct tty_struct *tty, struct file *file, 1328c2ecf20Sopenharmony_ci void *cookie, unsigned char *buf, size_t nr); 1338c2ecf20Sopenharmony_cistatic ssize_t r3964_write(struct tty_struct *tty, struct file *file, 1348c2ecf20Sopenharmony_ci const unsigned char *buf, size_t nr); 1358c2ecf20Sopenharmony_cistatic int r3964_ioctl(struct tty_struct *tty, struct file *file, 1368c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg); 1378c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1388c2ecf20Sopenharmony_cistatic int r3964_compat_ioctl(struct tty_struct *tty, struct file *file, 1398c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg); 1408c2ecf20Sopenharmony_ci#endif 1418c2ecf20Sopenharmony_cistatic void r3964_set_termios(struct tty_struct *tty, struct ktermios *old); 1428c2ecf20Sopenharmony_cistatic __poll_t r3964_poll(struct tty_struct *tty, struct file *file, 1438c2ecf20Sopenharmony_ci struct poll_table_struct *wait); 1448c2ecf20Sopenharmony_cistatic void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, 1458c2ecf20Sopenharmony_ci char *fp, int count); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops tty_ldisc_N_R3964 = { 1488c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1498c2ecf20Sopenharmony_ci .magic = TTY_LDISC_MAGIC, 1508c2ecf20Sopenharmony_ci .name = "R3964", 1518c2ecf20Sopenharmony_ci .open = r3964_open, 1528c2ecf20Sopenharmony_ci .close = r3964_close, 1538c2ecf20Sopenharmony_ci .read = r3964_read, 1548c2ecf20Sopenharmony_ci .write = r3964_write, 1558c2ecf20Sopenharmony_ci .ioctl = r3964_ioctl, 1568c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1578c2ecf20Sopenharmony_ci .compat_ioctl = r3964_compat_ioctl, 1588c2ecf20Sopenharmony_ci#endif 1598c2ecf20Sopenharmony_ci .set_termios = r3964_set_termios, 1608c2ecf20Sopenharmony_ci .poll = r3964_poll, 1618c2ecf20Sopenharmony_ci .receive_buf = r3964_receive_buf, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void dump_block(const unsigned char *block, unsigned int length) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned int i, j; 1678c2ecf20Sopenharmony_ci char linebuf[16 * 3 + 1]; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci for (i = 0; i < length; i += 16) { 1708c2ecf20Sopenharmony_ci for (j = 0; (j < 16) && (j + i < length); j++) { 1718c2ecf20Sopenharmony_ci sprintf(linebuf + 3 * j, "%02x ", block[i + j]); 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci linebuf[3 * j] = '\0'; 1748c2ecf20Sopenharmony_ci TRACE_PS("%s", linebuf); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/************************************************************* 1798c2ecf20Sopenharmony_ci * Driver initialisation 1808c2ecf20Sopenharmony_ci *************************************************************/ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/************************************************************* 1838c2ecf20Sopenharmony_ci * Module support routines 1848c2ecf20Sopenharmony_ci *************************************************************/ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void __exit r3964_exit(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int status; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci TRACE_M("cleanup_module()"); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci status = tty_unregister_ldisc(N_R3964); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (status != 0) { 1958c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964: error unregistering linediscipline: " 1968c2ecf20Sopenharmony_ci "%d\n", status); 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci TRACE_L("linediscipline successfully unregistered"); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int __init r3964_init(void) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int status; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci printk("r3964: Philips r3964 Driver $Revision: 1.10 $\n"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * Register the tty line discipline 2108c2ecf20Sopenharmony_ci */ 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci status = tty_register_ldisc(N_R3964, &tty_ldisc_N_R3964); 2138c2ecf20Sopenharmony_ci if (status == 0) { 2148c2ecf20Sopenharmony_ci TRACE_L("line discipline %d registered", N_R3964); 2158c2ecf20Sopenharmony_ci TRACE_L("flags=%x num=%x", tty_ldisc_N_R3964.flags, 2168c2ecf20Sopenharmony_ci tty_ldisc_N_R3964.num); 2178c2ecf20Sopenharmony_ci TRACE_L("open=%p", tty_ldisc_N_R3964.open); 2188c2ecf20Sopenharmony_ci TRACE_L("tty_ldisc_N_R3964 = %p", &tty_ldisc_N_R3964); 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964: error registering line discipline: " 2218c2ecf20Sopenharmony_ci "%d\n", status); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci return status; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cimodule_init(r3964_init); 2278c2ecf20Sopenharmony_cimodule_exit(r3964_exit); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/************************************************************* 2308c2ecf20Sopenharmony_ci * Protocol implementation routines 2318c2ecf20Sopenharmony_ci *************************************************************/ 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void add_tx_queue(struct r3964_info *pInfo, 2348c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci unsigned long flags; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci pHeader->next = NULL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (pInfo->tx_last == NULL) { 2438c2ecf20Sopenharmony_ci pInfo->tx_first = pInfo->tx_last = pHeader; 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci pInfo->tx_last->next = pHeader; 2468c2ecf20Sopenharmony_ci pInfo->tx_last = pHeader; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci TRACE_Q("add_tx_queue %p, length %d, tx_first = %p", 2528c2ecf20Sopenharmony_ci pHeader, pHeader->length, pInfo->tx_first); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void remove_from_tx_queue(struct r3964_info *pInfo, int error_code) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader; 2588c2ecf20Sopenharmony_ci unsigned long flags; 2598c2ecf20Sopenharmony_ci#ifdef DEBUG_QUEUE 2608c2ecf20Sopenharmony_ci struct r3964_block_header *pDump; 2618c2ecf20Sopenharmony_ci#endif 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci pHeader = pInfo->tx_first; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (pHeader == NULL) 2668c2ecf20Sopenharmony_ci return; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci#ifdef DEBUG_QUEUE 2698c2ecf20Sopenharmony_ci printk("r3964: remove_from_tx_queue: %p, length %u - ", 2708c2ecf20Sopenharmony_ci pHeader, pHeader->length); 2718c2ecf20Sopenharmony_ci for (pDump = pHeader; pDump; pDump = pDump->next) 2728c2ecf20Sopenharmony_ci printk("%p ", pDump); 2738c2ecf20Sopenharmony_ci printk("\n"); 2748c2ecf20Sopenharmony_ci#endif 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (pHeader->owner) { 2778c2ecf20Sopenharmony_ci if (error_code) { 2788c2ecf20Sopenharmony_ci add_msg(pHeader->owner, R3964_MSG_ACK, 0, 2798c2ecf20Sopenharmony_ci error_code, NULL); 2808c2ecf20Sopenharmony_ci } else { 2818c2ecf20Sopenharmony_ci add_msg(pHeader->owner, R3964_MSG_ACK, pHeader->length, 2828c2ecf20Sopenharmony_ci error_code, NULL); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci wake_up_interruptible(&pInfo->tty->read_wait); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci pInfo->tx_first = pHeader->next; 2908c2ecf20Sopenharmony_ci if (pInfo->tx_first == NULL) { 2918c2ecf20Sopenharmony_ci pInfo->tx_last = NULL; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci kfree(pHeader); 2978c2ecf20Sopenharmony_ci TRACE_M("remove_from_tx_queue - kfree %p", pHeader); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci TRACE_Q("remove_from_tx_queue: tx_first = %p, tx_last = %p", 3008c2ecf20Sopenharmony_ci pInfo->tx_first, pInfo->tx_last); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic void add_rx_queue(struct r3964_info *pInfo, 3048c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned long flags; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci pHeader->next = NULL; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (pInfo->rx_last == NULL) { 3138c2ecf20Sopenharmony_ci pInfo->rx_first = pInfo->rx_last = pHeader; 3148c2ecf20Sopenharmony_ci } else { 3158c2ecf20Sopenharmony_ci pInfo->rx_last->next = pHeader; 3168c2ecf20Sopenharmony_ci pInfo->rx_last = pHeader; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci pInfo->blocks_in_rx_queue++; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci TRACE_Q("add_rx_queue: %p, length = %d, rx_first = %p, count = %d", 3238c2ecf20Sopenharmony_ci pHeader, pHeader->length, 3248c2ecf20Sopenharmony_ci pInfo->rx_first, pInfo->blocks_in_rx_queue); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic void remove_from_rx_queue(struct r3964_info *pInfo, 3288c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci unsigned long flags; 3318c2ecf20Sopenharmony_ci struct r3964_block_header *pFind; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (pHeader == NULL) 3348c2ecf20Sopenharmony_ci return; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d", 3378c2ecf20Sopenharmony_ci pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue); 3388c2ecf20Sopenharmony_ci TRACE_Q("remove_from_rx_queue: %p, length %u", 3398c2ecf20Sopenharmony_ci pHeader, pHeader->length); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (pInfo->rx_first == pHeader) { 3448c2ecf20Sopenharmony_ci /* Remove the first block in the linked list: */ 3458c2ecf20Sopenharmony_ci pInfo->rx_first = pHeader->next; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (pInfo->rx_first == NULL) { 3488c2ecf20Sopenharmony_ci pInfo->rx_last = NULL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci pInfo->blocks_in_rx_queue--; 3518c2ecf20Sopenharmony_ci } else { 3528c2ecf20Sopenharmony_ci /* Find block to remove: */ 3538c2ecf20Sopenharmony_ci for (pFind = pInfo->rx_first; pFind; pFind = pFind->next) { 3548c2ecf20Sopenharmony_ci if (pFind->next == pHeader) { 3558c2ecf20Sopenharmony_ci /* Got it. */ 3568c2ecf20Sopenharmony_ci pFind->next = pHeader->next; 3578c2ecf20Sopenharmony_ci pInfo->blocks_in_rx_queue--; 3588c2ecf20Sopenharmony_ci if (pFind->next == NULL) { 3598c2ecf20Sopenharmony_ci /* Oh, removed the last one! */ 3608c2ecf20Sopenharmony_ci pInfo->rx_last = pFind; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci kfree(pHeader); 3708c2ecf20Sopenharmony_ci TRACE_M("remove_from_rx_queue - kfree %p", pHeader); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci TRACE_Q("remove_from_rx_queue: rx_first = %p, rx_last = %p, count = %d", 3738c2ecf20Sopenharmony_ci pInfo->rx_first, pInfo->rx_last, pInfo->blocks_in_rx_queue); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic void put_char(struct r3964_info *pInfo, unsigned char ch) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct tty_struct *tty = pInfo->tty; 3798c2ecf20Sopenharmony_ci /* FIXME: put_char should not be called from an IRQ */ 3808c2ecf20Sopenharmony_ci tty_put_char(tty, ch); 3818c2ecf20Sopenharmony_ci pInfo->bcc ^= ch; 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void flush(struct r3964_info *pInfo) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct tty_struct *tty = pInfo->tty; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (tty == NULL || tty->ops->flush_chars == NULL) 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci tty->ops->flush_chars(tty); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void trigger_transmit(struct r3964_info *pInfo) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci unsigned long flags; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if ((pInfo->state == R3964_IDLE) && (pInfo->tx_first != NULL)) { 4008c2ecf20Sopenharmony_ci pInfo->state = R3964_TX_REQUEST; 4018c2ecf20Sopenharmony_ci pInfo->nRetry = 0; 4028c2ecf20Sopenharmony_ci pInfo->flags &= ~R3964_ERROR; 4038c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci TRACE_PS("trigger_transmit - sent STX"); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci put_char(pInfo, STX); 4108c2ecf20Sopenharmony_ci flush(pInfo); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pInfo->bcc = 0; 4138c2ecf20Sopenharmony_ci } else { 4148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void retry_transmit(struct r3964_info *pInfo) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci if (pInfo->nRetry < R3964_MAX_RETRIES) { 4218c2ecf20Sopenharmony_ci TRACE_PE("transmission failed. Retry #%d", pInfo->nRetry); 4228c2ecf20Sopenharmony_ci pInfo->bcc = 0; 4238c2ecf20Sopenharmony_ci put_char(pInfo, STX); 4248c2ecf20Sopenharmony_ci flush(pInfo); 4258c2ecf20Sopenharmony_ci pInfo->state = R3964_TX_REQUEST; 4268c2ecf20Sopenharmony_ci pInfo->nRetry++; 4278c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); 4288c2ecf20Sopenharmony_ci } else { 4298c2ecf20Sopenharmony_ci TRACE_PE("transmission failed after %d retries", 4308c2ecf20Sopenharmony_ci R3964_MAX_RETRIES); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci remove_from_tx_queue(pInfo, R3964_TX_FAIL); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 4358c2ecf20Sopenharmony_ci flush(pInfo); 4368c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci trigger_transmit(pInfo); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic void transmit_block(struct r3964_info *pInfo) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct tty_struct *tty = pInfo->tty; 4458c2ecf20Sopenharmony_ci struct r3964_block_header *pBlock = pInfo->tx_first; 4468c2ecf20Sopenharmony_ci int room = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (tty == NULL || pBlock == NULL) { 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci room = tty_write_room(tty); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci TRACE_PS("transmit_block %p, room %d, length %d", 4558c2ecf20Sopenharmony_ci pBlock, room, pBlock->length); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci while (pInfo->tx_position < pBlock->length) { 4588c2ecf20Sopenharmony_ci if (room < 2) 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (pBlock->data[pInfo->tx_position] == DLE) { 4628c2ecf20Sopenharmony_ci /* send additional DLE char: */ 4638c2ecf20Sopenharmony_ci put_char(pInfo, DLE); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci put_char(pInfo, pBlock->data[pInfo->tx_position++]); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci room--; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if ((pInfo->tx_position == pBlock->length) && (room >= 3)) { 4718c2ecf20Sopenharmony_ci put_char(pInfo, DLE); 4728c2ecf20Sopenharmony_ci put_char(pInfo, ETX); 4738c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_BCC) { 4748c2ecf20Sopenharmony_ci put_char(pInfo, pInfo->bcc); 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci pInfo->state = R3964_WAIT_FOR_TX_ACK; 4778c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_QVZ); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci flush(pInfo); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic void on_receive_block(struct r3964_info *pInfo) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci unsigned int length; 4858c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 4868c2ecf20Sopenharmony_ci struct r3964_block_header *pBlock; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci length = pInfo->rx_position; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* compare byte checksum characters: */ 4918c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_BCC) { 4928c2ecf20Sopenharmony_ci if (pInfo->bcc != pInfo->last_rx) { 4938c2ecf20Sopenharmony_ci TRACE_PE("checksum error - got %x but expected %x", 4948c2ecf20Sopenharmony_ci pInfo->last_rx, pInfo->bcc); 4958c2ecf20Sopenharmony_ci pInfo->flags |= R3964_CHECKSUM; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* check for errors (parity, overrun,...): */ 5008c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_ERROR) { 5018c2ecf20Sopenharmony_ci TRACE_PE("on_receive_block - transmission failed error %x", 5028c2ecf20Sopenharmony_ci pInfo->flags & R3964_ERROR); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 5058c2ecf20Sopenharmony_ci flush(pInfo); 5068c2ecf20Sopenharmony_ci if (pInfo->nRetry < R3964_MAX_RETRIES) { 5078c2ecf20Sopenharmony_ci pInfo->state = R3964_WAIT_FOR_RX_REPEAT; 5088c2ecf20Sopenharmony_ci pInfo->nRetry++; 5098c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_RX_PANIC); 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci TRACE_PE("on_receive_block - failed after max retries"); 5128c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci return; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* received block; submit DLE: */ 5188c2ecf20Sopenharmony_ci put_char(pInfo, DLE); 5198c2ecf20Sopenharmony_ci flush(pInfo); 5208c2ecf20Sopenharmony_ci del_timer_sync(&pInfo->tmr); 5218c2ecf20Sopenharmony_ci TRACE_PS(" rx success: got %d chars", length); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* prepare struct r3964_block_header: */ 5248c2ecf20Sopenharmony_ci pBlock = kmalloc(length + sizeof(struct r3964_block_header), 5258c2ecf20Sopenharmony_ci GFP_KERNEL); 5268c2ecf20Sopenharmony_ci TRACE_M("on_receive_block - kmalloc %p", pBlock); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (pBlock == NULL) 5298c2ecf20Sopenharmony_ci return; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci pBlock->length = length; 5328c2ecf20Sopenharmony_ci pBlock->data = ((unsigned char *)pBlock) + 5338c2ecf20Sopenharmony_ci sizeof(struct r3964_block_header); 5348c2ecf20Sopenharmony_ci pBlock->locks = 0; 5358c2ecf20Sopenharmony_ci pBlock->next = NULL; 5368c2ecf20Sopenharmony_ci pBlock->owner = NULL; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci memcpy(pBlock->data, pInfo->rx_buf, length); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* queue block into rx_queue: */ 5418c2ecf20Sopenharmony_ci add_rx_queue(pInfo, pBlock); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* notify attached client processes: */ 5448c2ecf20Sopenharmony_ci for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) { 5458c2ecf20Sopenharmony_ci if (pClient->sig_flags & R3964_SIG_DATA) { 5468c2ecf20Sopenharmony_ci add_msg(pClient, R3964_MSG_DATA, length, R3964_OK, 5478c2ecf20Sopenharmony_ci pBlock); 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci wake_up_interruptible(&pInfo->tty->read_wait); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci trigger_transmit(pInfo); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_cistatic void receive_char(struct r3964_info *pInfo, const unsigned char c) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci switch (pInfo->state) { 5608c2ecf20Sopenharmony_ci case R3964_TX_REQUEST: 5618c2ecf20Sopenharmony_ci if (c == DLE) { 5628c2ecf20Sopenharmony_ci TRACE_PS("TX_REQUEST - got DLE"); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci pInfo->state = R3964_TRANSMITTING; 5658c2ecf20Sopenharmony_ci pInfo->tx_position = 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci transmit_block(pInfo); 5688c2ecf20Sopenharmony_ci } else if (c == STX) { 5698c2ecf20Sopenharmony_ci if (pInfo->nRetry == 0) { 5708c2ecf20Sopenharmony_ci TRACE_PE("TX_REQUEST - init conflict"); 5718c2ecf20Sopenharmony_ci if (pInfo->priority == R3964_SLAVE) { 5728c2ecf20Sopenharmony_ci goto start_receiving; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci TRACE_PE("TX_REQUEST - secondary init " 5768c2ecf20Sopenharmony_ci "conflict!? Switching to SLAVE mode " 5778c2ecf20Sopenharmony_ci "for next rx."); 5788c2ecf20Sopenharmony_ci goto start_receiving; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci } else { 5818c2ecf20Sopenharmony_ci TRACE_PE("TX_REQUEST - char != DLE: %x", c); 5828c2ecf20Sopenharmony_ci retry_transmit(pInfo); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci case R3964_TRANSMITTING: 5868c2ecf20Sopenharmony_ci if (c == NAK) { 5878c2ecf20Sopenharmony_ci TRACE_PE("TRANSMITTING - got NAK"); 5888c2ecf20Sopenharmony_ci retry_transmit(pInfo); 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci TRACE_PE("TRANSMITTING - got invalid char"); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci pInfo->state = R3964_WAIT_ZVZ_BEFORE_TX_RETRY; 5938c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_TX_ACK: 5978c2ecf20Sopenharmony_ci if (c == DLE) { 5988c2ecf20Sopenharmony_ci TRACE_PS("WAIT_FOR_TX_ACK - got DLE"); 5998c2ecf20Sopenharmony_ci remove_from_tx_queue(pInfo, R3964_OK); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 6028c2ecf20Sopenharmony_ci trigger_transmit(pInfo); 6038c2ecf20Sopenharmony_ci } else { 6048c2ecf20Sopenharmony_ci retry_transmit(pInfo); 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_RX_REPEAT: 6088c2ecf20Sopenharmony_ci case R3964_IDLE: 6098c2ecf20Sopenharmony_ci if (c == STX) { 6108c2ecf20Sopenharmony_ci /* Prevent rx_queue from overflow: */ 6118c2ecf20Sopenharmony_ci if (pInfo->blocks_in_rx_queue >= 6128c2ecf20Sopenharmony_ci R3964_MAX_BLOCKS_IN_RX_QUEUE) { 6138c2ecf20Sopenharmony_ci TRACE_PE("IDLE - got STX but no space in " 6148c2ecf20Sopenharmony_ci "rx_queue!"); 6158c2ecf20Sopenharmony_ci pInfo->state = R3964_WAIT_FOR_RX_BUF; 6168c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, 6178c2ecf20Sopenharmony_ci jiffies + R3964_TO_NO_BUF); 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_cistart_receiving: 6218c2ecf20Sopenharmony_ci /* Ok, start receiving: */ 6228c2ecf20Sopenharmony_ci TRACE_PS("IDLE - got STX"); 6238c2ecf20Sopenharmony_ci pInfo->rx_position = 0; 6248c2ecf20Sopenharmony_ci pInfo->last_rx = 0; 6258c2ecf20Sopenharmony_ci pInfo->flags &= ~R3964_ERROR; 6268c2ecf20Sopenharmony_ci pInfo->state = R3964_RECEIVING; 6278c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); 6288c2ecf20Sopenharmony_ci pInfo->nRetry = 0; 6298c2ecf20Sopenharmony_ci put_char(pInfo, DLE); 6308c2ecf20Sopenharmony_ci flush(pInfo); 6318c2ecf20Sopenharmony_ci pInfo->bcc = 0; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci case R3964_RECEIVING: 6358c2ecf20Sopenharmony_ci if (pInfo->rx_position < RX_BUF_SIZE) { 6368c2ecf20Sopenharmony_ci pInfo->bcc ^= c; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (c == DLE) { 6398c2ecf20Sopenharmony_ci if (pInfo->last_rx == DLE) { 6408c2ecf20Sopenharmony_ci pInfo->last_rx = 0; 6418c2ecf20Sopenharmony_ci goto char_to_buf; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci pInfo->last_rx = DLE; 6448c2ecf20Sopenharmony_ci break; 6458c2ecf20Sopenharmony_ci } else if ((c == ETX) && (pInfo->last_rx == DLE)) { 6468c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_BCC) { 6478c2ecf20Sopenharmony_ci pInfo->state = R3964_WAIT_FOR_BCC; 6488c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, 6498c2ecf20Sopenharmony_ci jiffies + R3964_TO_ZVZ); 6508c2ecf20Sopenharmony_ci } else { 6518c2ecf20Sopenharmony_ci on_receive_block(pInfo); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci } else { 6548c2ecf20Sopenharmony_ci pInfo->last_rx = c; 6558c2ecf20Sopenharmony_cichar_to_buf: 6568c2ecf20Sopenharmony_ci pInfo->rx_buf[pInfo->rx_position++] = c; 6578c2ecf20Sopenharmony_ci mod_timer(&pInfo->tmr, jiffies + R3964_TO_ZVZ); 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci /* else: overflow-msg? BUF_SIZE>MTU; should not happen? */ 6618c2ecf20Sopenharmony_ci break; 6628c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_BCC: 6638c2ecf20Sopenharmony_ci pInfo->last_rx = c; 6648c2ecf20Sopenharmony_ci on_receive_block(pInfo); 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void receive_error(struct r3964_info *pInfo, const char flag) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci switch (flag) { 6728c2ecf20Sopenharmony_ci case TTY_NORMAL: 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci case TTY_BREAK: 6758c2ecf20Sopenharmony_ci TRACE_PE("received break"); 6768c2ecf20Sopenharmony_ci pInfo->flags |= R3964_BREAK; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci case TTY_PARITY: 6798c2ecf20Sopenharmony_ci TRACE_PE("parity error"); 6808c2ecf20Sopenharmony_ci pInfo->flags |= R3964_PARITY; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci case TTY_FRAME: 6838c2ecf20Sopenharmony_ci TRACE_PE("frame error"); 6848c2ecf20Sopenharmony_ci pInfo->flags |= R3964_FRAME; 6858c2ecf20Sopenharmony_ci break; 6868c2ecf20Sopenharmony_ci case TTY_OVERRUN: 6878c2ecf20Sopenharmony_ci TRACE_PE("frame overrun"); 6888c2ecf20Sopenharmony_ci pInfo->flags |= R3964_OVERRUN; 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci default: 6918c2ecf20Sopenharmony_ci TRACE_PE("receive_error - unknown flag %d", flag); 6928c2ecf20Sopenharmony_ci pInfo->flags |= R3964_UNKNOWN; 6938c2ecf20Sopenharmony_ci break; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cistatic void on_timeout(struct timer_list *t) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci struct r3964_info *pInfo = from_timer(pInfo, t, tmr); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci switch (pInfo->state) { 7028c2ecf20Sopenharmony_ci case R3964_TX_REQUEST: 7038c2ecf20Sopenharmony_ci TRACE_PE("TX_REQUEST - timeout"); 7048c2ecf20Sopenharmony_ci retry_transmit(pInfo); 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case R3964_WAIT_ZVZ_BEFORE_TX_RETRY: 7078c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 7088c2ecf20Sopenharmony_ci flush(pInfo); 7098c2ecf20Sopenharmony_ci retry_transmit(pInfo); 7108c2ecf20Sopenharmony_ci break; 7118c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_TX_ACK: 7128c2ecf20Sopenharmony_ci TRACE_PE("WAIT_FOR_TX_ACK - timeout"); 7138c2ecf20Sopenharmony_ci retry_transmit(pInfo); 7148c2ecf20Sopenharmony_ci break; 7158c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_RX_BUF: 7168c2ecf20Sopenharmony_ci TRACE_PE("WAIT_FOR_RX_BUF - timeout"); 7178c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 7188c2ecf20Sopenharmony_ci flush(pInfo); 7198c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 7208c2ecf20Sopenharmony_ci break; 7218c2ecf20Sopenharmony_ci case R3964_RECEIVING: 7228c2ecf20Sopenharmony_ci TRACE_PE("RECEIVING - timeout after %d chars", 7238c2ecf20Sopenharmony_ci pInfo->rx_position); 7248c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 7258c2ecf20Sopenharmony_ci flush(pInfo); 7268c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 7278c2ecf20Sopenharmony_ci break; 7288c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_RX_REPEAT: 7298c2ecf20Sopenharmony_ci TRACE_PE("WAIT_FOR_RX_REPEAT - timeout"); 7308c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 7318c2ecf20Sopenharmony_ci break; 7328c2ecf20Sopenharmony_ci case R3964_WAIT_FOR_BCC: 7338c2ecf20Sopenharmony_ci TRACE_PE("WAIT_FOR_BCC - timeout"); 7348c2ecf20Sopenharmony_ci put_char(pInfo, NAK); 7358c2ecf20Sopenharmony_ci flush(pInfo); 7368c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic struct r3964_client_info *findClient(struct r3964_info *pInfo, 7428c2ecf20Sopenharmony_ci struct pid *pid) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci for (pClient = pInfo->firstClient; pClient; pClient = pClient->next) { 7478c2ecf20Sopenharmony_ci if (pClient->pid == pid) { 7488c2ecf20Sopenharmony_ci return pClient; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci return NULL; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cistatic int enable_signals(struct r3964_info *pInfo, struct pid *pid, int arg) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 7578c2ecf20Sopenharmony_ci struct r3964_client_info **ppClient; 7588c2ecf20Sopenharmony_ci struct r3964_message *pMsg; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if ((arg & R3964_SIG_ALL) == 0) { 7618c2ecf20Sopenharmony_ci /* Remove client from client list */ 7628c2ecf20Sopenharmony_ci for (ppClient = &pInfo->firstClient; *ppClient; 7638c2ecf20Sopenharmony_ci ppClient = &(*ppClient)->next) { 7648c2ecf20Sopenharmony_ci pClient = *ppClient; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (pClient->pid == pid) { 7678c2ecf20Sopenharmony_ci TRACE_PS("removing client %d from client list", 7688c2ecf20Sopenharmony_ci pid_nr(pid)); 7698c2ecf20Sopenharmony_ci *ppClient = pClient->next; 7708c2ecf20Sopenharmony_ci while (pClient->msg_count) { 7718c2ecf20Sopenharmony_ci pMsg = remove_msg(pInfo, pClient); 7728c2ecf20Sopenharmony_ci if (pMsg) { 7738c2ecf20Sopenharmony_ci kfree(pMsg); 7748c2ecf20Sopenharmony_ci TRACE_M("enable_signals - msg " 7758c2ecf20Sopenharmony_ci "kfree %p", pMsg); 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci put_pid(pClient->pid); 7798c2ecf20Sopenharmony_ci kfree(pClient); 7808c2ecf20Sopenharmony_ci TRACE_M("enable_signals - kfree %p", pClient); 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci } 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci return -EINVAL; 7858c2ecf20Sopenharmony_ci } else { 7868c2ecf20Sopenharmony_ci pClient = findClient(pInfo, pid); 7878c2ecf20Sopenharmony_ci if (pClient) { 7888c2ecf20Sopenharmony_ci /* update signal options */ 7898c2ecf20Sopenharmony_ci pClient->sig_flags = arg; 7908c2ecf20Sopenharmony_ci } else { 7918c2ecf20Sopenharmony_ci /* add client to client list */ 7928c2ecf20Sopenharmony_ci pClient = kmalloc(sizeof(struct r3964_client_info), 7938c2ecf20Sopenharmony_ci GFP_KERNEL); 7948c2ecf20Sopenharmony_ci TRACE_M("enable_signals - kmalloc %p", pClient); 7958c2ecf20Sopenharmony_ci if (pClient == NULL) 7968c2ecf20Sopenharmony_ci return -ENOMEM; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci TRACE_PS("add client %d to client list", pid_nr(pid)); 7998c2ecf20Sopenharmony_ci spin_lock_init(&pClient->lock); 8008c2ecf20Sopenharmony_ci pClient->sig_flags = arg; 8018c2ecf20Sopenharmony_ci pClient->pid = get_pid(pid); 8028c2ecf20Sopenharmony_ci pClient->next = pInfo->firstClient; 8038c2ecf20Sopenharmony_ci pClient->first_msg = NULL; 8048c2ecf20Sopenharmony_ci pClient->last_msg = NULL; 8058c2ecf20Sopenharmony_ci pClient->next_block_to_read = NULL; 8068c2ecf20Sopenharmony_ci pClient->msg_count = 0; 8078c2ecf20Sopenharmony_ci pInfo->firstClient = pClient; 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci return 0; 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_cistatic int read_telegram(struct r3964_info *pInfo, struct pid *pid, 8158c2ecf20Sopenharmony_ci unsigned char __user * buf) 8168c2ecf20Sopenharmony_ci{ 8178c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 8188c2ecf20Sopenharmony_ci struct r3964_block_header *block; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (!buf) { 8218c2ecf20Sopenharmony_ci return -EINVAL; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci pClient = findClient(pInfo, pid); 8258c2ecf20Sopenharmony_ci if (pClient == NULL) { 8268c2ecf20Sopenharmony_ci return -EINVAL; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci block = pClient->next_block_to_read; 8308c2ecf20Sopenharmony_ci if (!block) { 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci } else { 8338c2ecf20Sopenharmony_ci if (copy_to_user(buf, block->data, block->length)) 8348c2ecf20Sopenharmony_ci return -EFAULT; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci remove_client_block(pInfo, pClient); 8378c2ecf20Sopenharmony_ci return block->length; 8388c2ecf20Sopenharmony_ci } 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic void add_msg(struct r3964_client_info *pClient, int msg_id, int arg, 8448c2ecf20Sopenharmony_ci int error_code, struct r3964_block_header *pBlock) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct r3964_message *pMsg; 8478c2ecf20Sopenharmony_ci unsigned long flags; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (pClient->msg_count < R3964_MAX_MSG_COUNT - 1) { 8508c2ecf20Sopenharmony_ciqueue_the_message: 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci pMsg = kmalloc(sizeof(struct r3964_message), 8538c2ecf20Sopenharmony_ci error_code ? GFP_ATOMIC : GFP_KERNEL); 8548c2ecf20Sopenharmony_ci TRACE_M("add_msg - kmalloc %p", pMsg); 8558c2ecf20Sopenharmony_ci if (pMsg == NULL) { 8568c2ecf20Sopenharmony_ci return; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci spin_lock_irqsave(&pClient->lock, flags); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci pMsg->msg_id = msg_id; 8628c2ecf20Sopenharmony_ci pMsg->arg = arg; 8638c2ecf20Sopenharmony_ci pMsg->error_code = error_code; 8648c2ecf20Sopenharmony_ci pMsg->block = pBlock; 8658c2ecf20Sopenharmony_ci pMsg->next = NULL; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (pClient->last_msg == NULL) { 8688c2ecf20Sopenharmony_ci pClient->first_msg = pClient->last_msg = pMsg; 8698c2ecf20Sopenharmony_ci } else { 8708c2ecf20Sopenharmony_ci pClient->last_msg->next = pMsg; 8718c2ecf20Sopenharmony_ci pClient->last_msg = pMsg; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci pClient->msg_count++; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (pBlock != NULL) { 8778c2ecf20Sopenharmony_ci pBlock->locks++; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pClient->lock, flags); 8808c2ecf20Sopenharmony_ci } else { 8818c2ecf20Sopenharmony_ci if ((pClient->last_msg->msg_id == R3964_MSG_ACK) 8828c2ecf20Sopenharmony_ci && (pClient->last_msg->error_code == R3964_OVERFLOW)) { 8838c2ecf20Sopenharmony_ci pClient->last_msg->arg++; 8848c2ecf20Sopenharmony_ci TRACE_PE("add_msg - inc prev OVERFLOW-msg"); 8858c2ecf20Sopenharmony_ci } else { 8868c2ecf20Sopenharmony_ci msg_id = R3964_MSG_ACK; 8878c2ecf20Sopenharmony_ci arg = 0; 8888c2ecf20Sopenharmony_ci error_code = R3964_OVERFLOW; 8898c2ecf20Sopenharmony_ci pBlock = NULL; 8908c2ecf20Sopenharmony_ci TRACE_PE("add_msg - queue OVERFLOW-msg"); 8918c2ecf20Sopenharmony_ci goto queue_the_message; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci /* Send SIGIO signal to client process: */ 8958c2ecf20Sopenharmony_ci if (pClient->sig_flags & R3964_USE_SIGIO) { 8968c2ecf20Sopenharmony_ci kill_pid(pClient->pid, SIGIO, 1); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic struct r3964_message *remove_msg(struct r3964_info *pInfo, 9018c2ecf20Sopenharmony_ci struct r3964_client_info *pClient) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci struct r3964_message *pMsg = NULL; 9048c2ecf20Sopenharmony_ci unsigned long flags; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (pClient->first_msg) { 9078c2ecf20Sopenharmony_ci spin_lock_irqsave(&pClient->lock, flags); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci pMsg = pClient->first_msg; 9108c2ecf20Sopenharmony_ci pClient->first_msg = pMsg->next; 9118c2ecf20Sopenharmony_ci if (pClient->first_msg == NULL) { 9128c2ecf20Sopenharmony_ci pClient->last_msg = NULL; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci pClient->msg_count--; 9168c2ecf20Sopenharmony_ci if (pMsg->block) { 9178c2ecf20Sopenharmony_ci remove_client_block(pInfo, pClient); 9188c2ecf20Sopenharmony_ci pClient->next_block_to_read = pMsg->block; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pClient->lock, flags); 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci return pMsg; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_cistatic void remove_client_block(struct r3964_info *pInfo, 9268c2ecf20Sopenharmony_ci struct r3964_client_info *pClient) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct r3964_block_header *block; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci TRACE_PS("remove_client_block PID %d", pid_nr(pClient->pid)); 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci block = pClient->next_block_to_read; 9338c2ecf20Sopenharmony_ci if (block) { 9348c2ecf20Sopenharmony_ci block->locks--; 9358c2ecf20Sopenharmony_ci if (block->locks == 0) { 9368c2ecf20Sopenharmony_ci remove_from_rx_queue(pInfo, block); 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci pClient->next_block_to_read = NULL; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/************************************************************* 9438c2ecf20Sopenharmony_ci * Line discipline routines 9448c2ecf20Sopenharmony_ci *************************************************************/ 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic int r3964_open(struct tty_struct *tty) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci struct r3964_info *pInfo; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci TRACE_L("open"); 9518c2ecf20Sopenharmony_ci TRACE_L("tty=%p, PID=%d, disc_data=%p", 9528c2ecf20Sopenharmony_ci tty, current->pid, tty->disc_data); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci pInfo = kmalloc(sizeof(struct r3964_info), GFP_KERNEL); 9558c2ecf20Sopenharmony_ci TRACE_M("r3964_open - info kmalloc %p", pInfo); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (!pInfo) { 9588c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964: failed to alloc info structure\n"); 9598c2ecf20Sopenharmony_ci return -ENOMEM; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci pInfo->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); 9638c2ecf20Sopenharmony_ci TRACE_M("r3964_open - rx_buf kmalloc %p", pInfo->rx_buf); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (!pInfo->rx_buf) { 9668c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964: failed to alloc receive buffer\n"); 9678c2ecf20Sopenharmony_ci kfree(pInfo); 9688c2ecf20Sopenharmony_ci TRACE_M("r3964_open - info kfree %p", pInfo); 9698c2ecf20Sopenharmony_ci return -ENOMEM; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci pInfo->tx_buf = kmalloc(TX_BUF_SIZE, GFP_KERNEL); 9738c2ecf20Sopenharmony_ci TRACE_M("r3964_open - tx_buf kmalloc %p", pInfo->tx_buf); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (!pInfo->tx_buf) { 9768c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964: failed to alloc transmit buffer\n"); 9778c2ecf20Sopenharmony_ci kfree(pInfo->rx_buf); 9788c2ecf20Sopenharmony_ci TRACE_M("r3964_open - rx_buf kfree %p", pInfo->rx_buf); 9798c2ecf20Sopenharmony_ci kfree(pInfo); 9808c2ecf20Sopenharmony_ci TRACE_M("r3964_open - info kfree %p", pInfo); 9818c2ecf20Sopenharmony_ci return -ENOMEM; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci spin_lock_init(&pInfo->lock); 9858c2ecf20Sopenharmony_ci mutex_init(&pInfo->read_lock); 9868c2ecf20Sopenharmony_ci pInfo->tty = tty; 9878c2ecf20Sopenharmony_ci pInfo->priority = R3964_MASTER; 9888c2ecf20Sopenharmony_ci pInfo->rx_first = pInfo->rx_last = NULL; 9898c2ecf20Sopenharmony_ci pInfo->tx_first = pInfo->tx_last = NULL; 9908c2ecf20Sopenharmony_ci pInfo->rx_position = 0; 9918c2ecf20Sopenharmony_ci pInfo->tx_position = 0; 9928c2ecf20Sopenharmony_ci pInfo->last_rx = 0; 9938c2ecf20Sopenharmony_ci pInfo->blocks_in_rx_queue = 0; 9948c2ecf20Sopenharmony_ci pInfo->firstClient = NULL; 9958c2ecf20Sopenharmony_ci pInfo->state = R3964_IDLE; 9968c2ecf20Sopenharmony_ci pInfo->flags = R3964_DEBUG; 9978c2ecf20Sopenharmony_ci pInfo->nRetry = 0; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci tty->disc_data = pInfo; 10008c2ecf20Sopenharmony_ci tty->receive_room = 65536; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci timer_setup(&pInfo->tmr, on_timeout, 0); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return 0; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_cistatic void r3964_close(struct tty_struct *tty) 10088c2ecf20Sopenharmony_ci{ 10098c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 10108c2ecf20Sopenharmony_ci struct r3964_client_info *pClient, *pNext; 10118c2ecf20Sopenharmony_ci struct r3964_message *pMsg; 10128c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader, *pNextHeader; 10138c2ecf20Sopenharmony_ci unsigned long flags; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci TRACE_L("close"); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* 10188c2ecf20Sopenharmony_ci * Make sure that our task queue isn't activated. If it 10198c2ecf20Sopenharmony_ci * is, take it out of the linked list. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci del_timer_sync(&pInfo->tmr); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* Remove client-structs and message queues: */ 10248c2ecf20Sopenharmony_ci pClient = pInfo->firstClient; 10258c2ecf20Sopenharmony_ci while (pClient) { 10268c2ecf20Sopenharmony_ci pNext = pClient->next; 10278c2ecf20Sopenharmony_ci while (pClient->msg_count) { 10288c2ecf20Sopenharmony_ci pMsg = remove_msg(pInfo, pClient); 10298c2ecf20Sopenharmony_ci if (pMsg) { 10308c2ecf20Sopenharmony_ci kfree(pMsg); 10318c2ecf20Sopenharmony_ci TRACE_M("r3964_close - msg kfree %p", pMsg); 10328c2ecf20Sopenharmony_ci } 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci put_pid(pClient->pid); 10358c2ecf20Sopenharmony_ci kfree(pClient); 10368c2ecf20Sopenharmony_ci TRACE_M("r3964_close - client kfree %p", pClient); 10378c2ecf20Sopenharmony_ci pClient = pNext; 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci /* Remove jobs from tx_queue: */ 10408c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 10418c2ecf20Sopenharmony_ci pHeader = pInfo->tx_first; 10428c2ecf20Sopenharmony_ci pInfo->tx_first = pInfo->tx_last = NULL; 10438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci while (pHeader) { 10468c2ecf20Sopenharmony_ci pNextHeader = pHeader->next; 10478c2ecf20Sopenharmony_ci kfree(pHeader); 10488c2ecf20Sopenharmony_ci pHeader = pNextHeader; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Free buffers: */ 10528c2ecf20Sopenharmony_ci kfree(pInfo->rx_buf); 10538c2ecf20Sopenharmony_ci TRACE_M("r3964_close - rx_buf kfree %p", pInfo->rx_buf); 10548c2ecf20Sopenharmony_ci kfree(pInfo->tx_buf); 10558c2ecf20Sopenharmony_ci TRACE_M("r3964_close - tx_buf kfree %p", pInfo->tx_buf); 10568c2ecf20Sopenharmony_ci kfree(pInfo); 10578c2ecf20Sopenharmony_ci TRACE_M("r3964_close - info kfree %p", pInfo); 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic ssize_t r3964_read(struct tty_struct *tty, struct file *file, 10618c2ecf20Sopenharmony_ci unsigned char *kbuf, size_t nr, 10628c2ecf20Sopenharmony_ci void **cookie, unsigned long offset) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 10658c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 10668c2ecf20Sopenharmony_ci struct r3964_message *pMsg; 10678c2ecf20Sopenharmony_ci struct r3964_client_message theMsg; 10688c2ecf20Sopenharmony_ci int ret; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci TRACE_L("read()"); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci /* 10738c2ecf20Sopenharmony_ci * Internal serialization of reads. 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_ci if (file->f_flags & O_NONBLOCK) { 10768c2ecf20Sopenharmony_ci if (!mutex_trylock(&pInfo->read_lock)) 10778c2ecf20Sopenharmony_ci return -EAGAIN; 10788c2ecf20Sopenharmony_ci } else { 10798c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&pInfo->read_lock)) 10808c2ecf20Sopenharmony_ci return -ERESTARTSYS; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci pClient = findClient(pInfo, task_pid(current)); 10848c2ecf20Sopenharmony_ci if (pClient) { 10858c2ecf20Sopenharmony_ci pMsg = remove_msg(pInfo, pClient); 10868c2ecf20Sopenharmony_ci if (pMsg == NULL) { 10878c2ecf20Sopenharmony_ci /* no messages available. */ 10888c2ecf20Sopenharmony_ci if (tty_io_nonblock(tty, file)) { 10898c2ecf20Sopenharmony_ci ret = -EAGAIN; 10908c2ecf20Sopenharmony_ci goto unlock; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci /* block until there is a message: */ 10938c2ecf20Sopenharmony_ci wait_event_interruptible(tty->read_wait, 10948c2ecf20Sopenharmony_ci (pMsg = remove_msg(pInfo, pClient))); 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* If we still haven't got a message, we must have been signalled */ 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (!pMsg) { 11008c2ecf20Sopenharmony_ci ret = -EINTR; 11018c2ecf20Sopenharmony_ci goto unlock; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* deliver msg to client process: */ 11058c2ecf20Sopenharmony_ci theMsg.msg_id = pMsg->msg_id; 11068c2ecf20Sopenharmony_ci theMsg.arg = pMsg->arg; 11078c2ecf20Sopenharmony_ci theMsg.error_code = pMsg->error_code; 11088c2ecf20Sopenharmony_ci ret = sizeof(struct r3964_client_message); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci kfree(pMsg); 11118c2ecf20Sopenharmony_ci TRACE_M("r3964_read - msg kfree %p", pMsg); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci memcpy(kbuf, &theMsg, ret); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci TRACE_PS("read - return %d", ret); 11168c2ecf20Sopenharmony_ci goto unlock; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci ret = -EPERM; 11198c2ecf20Sopenharmony_ciunlock: 11208c2ecf20Sopenharmony_ci mutex_unlock(&pInfo->read_lock); 11218c2ecf20Sopenharmony_ci return ret; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic ssize_t r3964_write(struct tty_struct *tty, struct file *file, 11258c2ecf20Sopenharmony_ci const unsigned char *data, size_t count) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 11288c2ecf20Sopenharmony_ci struct r3964_block_header *pHeader; 11298c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 11308c2ecf20Sopenharmony_ci unsigned char *new_data; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci TRACE_L("write request, %d characters", count); 11338c2ecf20Sopenharmony_ci/* 11348c2ecf20Sopenharmony_ci * Verify the pointers 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (!pInfo) 11388c2ecf20Sopenharmony_ci return -EIO; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci/* 11418c2ecf20Sopenharmony_ci * Ensure that the caller does not wish to send too much. 11428c2ecf20Sopenharmony_ci */ 11438c2ecf20Sopenharmony_ci if (count > R3964_MTU) { 11448c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_DEBUG) { 11458c2ecf20Sopenharmony_ci TRACE_L(KERN_WARNING "r3964_write: truncating user " 11468c2ecf20Sopenharmony_ci "packet from %u to mtu %d", count, R3964_MTU); 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci count = R3964_MTU; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci/* 11518c2ecf20Sopenharmony_ci * Allocate a buffer for the data and copy it from the buffer with header prepended 11528c2ecf20Sopenharmony_ci */ 11538c2ecf20Sopenharmony_ci new_data = kmalloc(count + sizeof(struct r3964_block_header), 11548c2ecf20Sopenharmony_ci GFP_KERNEL); 11558c2ecf20Sopenharmony_ci TRACE_M("r3964_write - kmalloc %p", new_data); 11568c2ecf20Sopenharmony_ci if (new_data == NULL) { 11578c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_DEBUG) { 11588c2ecf20Sopenharmony_ci printk(KERN_ERR "r3964_write: no memory\n"); 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci return -ENOSPC; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci pHeader = (struct r3964_block_header *)new_data; 11648c2ecf20Sopenharmony_ci pHeader->data = new_data + sizeof(struct r3964_block_header); 11658c2ecf20Sopenharmony_ci pHeader->length = count; 11668c2ecf20Sopenharmony_ci pHeader->locks = 0; 11678c2ecf20Sopenharmony_ci pHeader->owner = NULL; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci pClient = findClient(pInfo, task_pid(current)); 11708c2ecf20Sopenharmony_ci if (pClient) { 11718c2ecf20Sopenharmony_ci pHeader->owner = pClient; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci memcpy(pHeader->data, data, count); /* We already verified this */ 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (pInfo->flags & R3964_DEBUG) { 11778c2ecf20Sopenharmony_ci dump_block(pHeader->data, count); 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci/* 11818c2ecf20Sopenharmony_ci * Add buffer to transmit-queue: 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ci add_tx_queue(pInfo, pHeader); 11848c2ecf20Sopenharmony_ci trigger_transmit(pInfo); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic int r3964_ioctl(struct tty_struct *tty, struct file *file, 11908c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 11938c2ecf20Sopenharmony_ci if (pInfo == NULL) 11948c2ecf20Sopenharmony_ci return -EINVAL; 11958c2ecf20Sopenharmony_ci switch (cmd) { 11968c2ecf20Sopenharmony_ci case R3964_ENABLE_SIGNALS: 11978c2ecf20Sopenharmony_ci return enable_signals(pInfo, task_pid(current), arg); 11988c2ecf20Sopenharmony_ci case R3964_SETPRIORITY: 11998c2ecf20Sopenharmony_ci if (arg < R3964_MASTER || arg > R3964_SLAVE) 12008c2ecf20Sopenharmony_ci return -EINVAL; 12018c2ecf20Sopenharmony_ci pInfo->priority = arg & 0xff; 12028c2ecf20Sopenharmony_ci return 0; 12038c2ecf20Sopenharmony_ci case R3964_USE_BCC: 12048c2ecf20Sopenharmony_ci if (arg) 12058c2ecf20Sopenharmony_ci pInfo->flags |= R3964_BCC; 12068c2ecf20Sopenharmony_ci else 12078c2ecf20Sopenharmony_ci pInfo->flags &= ~R3964_BCC; 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci case R3964_READ_TELEGRAM: 12108c2ecf20Sopenharmony_ci return read_telegram(pInfo, task_pid(current), 12118c2ecf20Sopenharmony_ci (unsigned char __user *)arg); 12128c2ecf20Sopenharmony_ci default: 12138c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 12188c2ecf20Sopenharmony_cistatic int r3964_compat_ioctl(struct tty_struct *tty, struct file *file, 12198c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci switch (cmd) { 12228c2ecf20Sopenharmony_ci case R3964_ENABLE_SIGNALS: 12238c2ecf20Sopenharmony_ci case R3964_SETPRIORITY: 12248c2ecf20Sopenharmony_ci case R3964_USE_BCC: 12258c2ecf20Sopenharmony_ci return r3964_ioctl(tty, file, cmd, arg); 12268c2ecf20Sopenharmony_ci default: 12278c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci#endif 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic void r3964_set_termios(struct tty_struct *tty, struct ktermios *old) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci TRACE_L("set_termios"); 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci/* Called without the kernel lock held - fine */ 12388c2ecf20Sopenharmony_cistatic __poll_t r3964_poll(struct tty_struct *tty, struct file *file, 12398c2ecf20Sopenharmony_ci struct poll_table_struct *wait) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 12428c2ecf20Sopenharmony_ci struct r3964_client_info *pClient; 12438c2ecf20Sopenharmony_ci struct r3964_message *pMsg = NULL; 12448c2ecf20Sopenharmony_ci unsigned long flags; 12458c2ecf20Sopenharmony_ci __poll_t result = EPOLLOUT; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci TRACE_L("POLL"); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci pClient = findClient(pInfo, task_pid(current)); 12508c2ecf20Sopenharmony_ci if (pClient) { 12518c2ecf20Sopenharmony_ci poll_wait(file, &tty->read_wait, wait); 12528c2ecf20Sopenharmony_ci spin_lock_irqsave(&pInfo->lock, flags); 12538c2ecf20Sopenharmony_ci pMsg = pClient->first_msg; 12548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pInfo->lock, flags); 12558c2ecf20Sopenharmony_ci if (pMsg) 12568c2ecf20Sopenharmony_ci result |= EPOLLIN | EPOLLRDNORM; 12578c2ecf20Sopenharmony_ci } else { 12588c2ecf20Sopenharmony_ci result = -EINVAL; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci return result; 12618c2ecf20Sopenharmony_ci} 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_cistatic void r3964_receive_buf(struct tty_struct *tty, const unsigned char *cp, 12648c2ecf20Sopenharmony_ci char *fp, int count) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct r3964_info *pInfo = tty->disc_data; 12678c2ecf20Sopenharmony_ci const unsigned char *p; 12688c2ecf20Sopenharmony_ci char *f, flags = TTY_NORMAL; 12698c2ecf20Sopenharmony_ci int i; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci for (i = count, p = cp, f = fp; i; i--, p++) { 12728c2ecf20Sopenharmony_ci if (f) 12738c2ecf20Sopenharmony_ci flags = *f++; 12748c2ecf20Sopenharmony_ci if (flags == TTY_NORMAL) { 12758c2ecf20Sopenharmony_ci receive_char(pInfo, *p); 12768c2ecf20Sopenharmony_ci } else { 12778c2ecf20Sopenharmony_ci receive_error(pInfo, flags); 12788c2ecf20Sopenharmony_ci } 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci } 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 12848c2ecf20Sopenharmony_ciMODULE_ALIAS_LDISC(N_R3964); 1285