1 |
1275 |
phoenix |
/*
|
2 |
|
|
*
|
3 |
|
|
* (c) 1999 by Computone Corporation
|
4 |
|
|
*
|
5 |
|
|
********************************************************************************
|
6 |
|
|
*
|
7 |
|
|
* PACKAGE: Linux tty Device Driver for IntelliPort family of multiport
|
8 |
|
|
* serial I/O controllers.
|
9 |
|
|
*
|
10 |
|
|
* DESCRIPTION: Mainline code for the device driver
|
11 |
|
|
*
|
12 |
|
|
*******************************************************************************/
|
13 |
|
|
// ToDo:
|
14 |
|
|
//
|
15 |
|
|
// Fix the immediate DSS_NOW problem.
|
16 |
|
|
// Work over the channel stats return logic in ip2_ipl_ioctl so they
|
17 |
|
|
// make sense for all 256 possible channels and so the user space
|
18 |
|
|
// utilities will compile and work properly.
|
19 |
|
|
//
|
20 |
|
|
// Done:
|
21 |
|
|
//
|
22 |
|
|
// 1.2.14 /\/\|=mhw=|\/\/
|
23 |
|
|
// Added bounds checking to ip2_ipl_ioctl to avoid potential terroristic acts.
|
24 |
|
|
// Changed the definition of ip2trace to be more consistant with kernel style
|
25 |
|
|
// Thanks to Andreas Dilger <adilger@turbolabs.com> for these updates
|
26 |
|
|
//
|
27 |
|
|
// 1.2.13 /\/\|=mhw=|\/\/
|
28 |
|
|
// DEVFS: Renamed ttf/{n} to tts/F{n} and cuf/{n} to cua/F{n} to conform
|
29 |
|
|
// to agreed devfs serial device naming convention.
|
30 |
|
|
//
|
31 |
|
|
// 1.2.12 /\/\|=mhw=|\/\/
|
32 |
|
|
// Cleaned up some remove queue cut and paste errors
|
33 |
|
|
//
|
34 |
|
|
// 1.2.11 /\/\|=mhw=|\/\/
|
35 |
|
|
// Clean up potential NULL pointer dereferences
|
36 |
|
|
// Clean up devfs registration
|
37 |
|
|
// Add kernel command line parsing for io and irq
|
38 |
|
|
// Compile defaults for io and irq are now set in ip2.c not ip2/ip2.h!
|
39 |
|
|
// Reworked poll_only hack for explicit parameter setting
|
40 |
|
|
// You must now EXPLICITLY set poll_only = 1 or set all irqs to 0
|
41 |
|
|
// Merged ip2_loadmain and old_ip2_init
|
42 |
|
|
// Converted all instances of interruptible_sleep_on into queue calls
|
43 |
|
|
// Most of these had no race conditions but better to clean up now
|
44 |
|
|
//
|
45 |
|
|
// 1.2.10 /\/\|=mhw=|\/\/
|
46 |
|
|
// Fixed the bottom half interrupt handler and enabled USE_IQI
|
47 |
|
|
// to split the interrupt handler into a formal top-half / bottom-half
|
48 |
|
|
// Fixed timing window on high speed processors that queued messages to
|
49 |
|
|
// the outbound mail fifo faster than the board could handle.
|
50 |
|
|
//
|
51 |
|
|
// 1.2.9
|
52 |
|
|
// Four box EX was barfing on >128k kmalloc, made structure smaller by
|
53 |
|
|
// reducing output buffer size
|
54 |
|
|
//
|
55 |
|
|
// 1.2.8
|
56 |
|
|
// Device file system support (MHW)
|
57 |
|
|
//
|
58 |
|
|
// 1.2.7
|
59 |
|
|
// Fixed
|
60 |
|
|
// Reload of ip2 without unloading ip2main hangs system on cat of /proc/modules
|
61 |
|
|
//
|
62 |
|
|
// 1.2.6
|
63 |
|
|
//Fixes DCD problems
|
64 |
|
|
// DCD was not reported when CLOCAL was set on call to TIOCMGET
|
65 |
|
|
//
|
66 |
|
|
//Enhancements:
|
67 |
|
|
// TIOCMGET requests and waits for status return
|
68 |
|
|
// No DSS interrupts enabled except for DCD when needed
|
69 |
|
|
//
|
70 |
|
|
// For internal use only
|
71 |
|
|
//
|
72 |
|
|
//#define IP2DEBUG_INIT
|
73 |
|
|
//#define IP2DEBUG_OPEN
|
74 |
|
|
//#define IP2DEBUG_WRITE
|
75 |
|
|
//#define IP2DEBUG_READ
|
76 |
|
|
//#define IP2DEBUG_IOCTL
|
77 |
|
|
//#define IP2DEBUG_IPL
|
78 |
|
|
|
79 |
|
|
//#define IP2DEBUG_TRACE
|
80 |
|
|
//#define DEBUG_FIFO
|
81 |
|
|
|
82 |
|
|
/************/
|
83 |
|
|
/* Includes */
|
84 |
|
|
/************/
|
85 |
|
|
#include <linux/config.h>
|
86 |
|
|
// Uncomment the following if you want it compiled with modversions
|
87 |
|
|
|
88 |
|
|
#include <linux/version.h>
|
89 |
|
|
|
90 |
|
|
#include <linux/ctype.h>
|
91 |
|
|
#include <linux/string.h>
|
92 |
|
|
#include <linux/fcntl.h>
|
93 |
|
|
#include <linux/errno.h>
|
94 |
|
|
#include <linux/module.h>
|
95 |
|
|
#include <linux/signal.h>
|
96 |
|
|
#include <linux/sched.h>
|
97 |
|
|
#ifdef CONFIG_DEVFS_FS
|
98 |
|
|
#include <linux/devfs_fs_kernel.h>
|
99 |
|
|
#endif
|
100 |
|
|
#include <linux/timer.h>
|
101 |
|
|
#include <linux/interrupt.h>
|
102 |
|
|
#include <linux/pci.h>
|
103 |
|
|
#include <linux/mm.h>
|
104 |
|
|
#include <linux/slab.h>
|
105 |
|
|
#include <linux/major.h>
|
106 |
|
|
#include <linux/wait.h>
|
107 |
|
|
|
108 |
|
|
#include <linux/tty.h>
|
109 |
|
|
#include <linux/tty_flip.h>
|
110 |
|
|
#include <linux/termios.h>
|
111 |
|
|
#include <linux/tty_driver.h>
|
112 |
|
|
#include <linux/serial.h>
|
113 |
|
|
#include <linux/ptrace.h>
|
114 |
|
|
#include <linux/ioport.h>
|
115 |
|
|
|
116 |
|
|
#include <linux/cdk.h>
|
117 |
|
|
#include <linux/comstats.h>
|
118 |
|
|
#include <linux/delay.h>
|
119 |
|
|
|
120 |
|
|
#include <asm/system.h>
|
121 |
|
|
#include <asm/io.h>
|
122 |
|
|
#include <asm/irq.h>
|
123 |
|
|
#include <asm/bitops.h>
|
124 |
|
|
|
125 |
|
|
#ifndef KERNEL_VERSION
|
126 |
|
|
#define KERNEL_VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq))
|
127 |
|
|
#endif
|
128 |
|
|
|
129 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
|
130 |
|
|
# include <linux/vmalloc.h>
|
131 |
|
|
# include <linux/init.h>
|
132 |
|
|
# include <asm/serial.h>
|
133 |
|
|
#else
|
134 |
|
|
# include <linux/bios32.h>
|
135 |
|
|
#endif
|
136 |
|
|
|
137 |
|
|
// These VERSION switches maybe inexact because I simply don't know
|
138 |
|
|
// when the various features appeared in the 2.1.XX kernels.
|
139 |
|
|
// They are good enough for 2.0 vs 2.2 and if you are fooling with
|
140 |
|
|
// the 2.1.XX stuff then it would be trivial for you to fix.
|
141 |
|
|
// Most of these macros were stolen from some other drivers
|
142 |
|
|
// so blame them.
|
143 |
|
|
|
144 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,4)
|
145 |
|
|
# include <asm/segment.h>
|
146 |
|
|
# define GET_USER(error,value,addr) error = get_user(value,addr)
|
147 |
|
|
# define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0
|
148 |
|
|
# define PUT_USER(error,value,addr) error = put_user(value,addr)
|
149 |
|
|
# define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0
|
150 |
|
|
|
151 |
|
|
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,5)
|
152 |
|
|
# include <asm/uaccess.h>
|
153 |
|
|
# define pcibios_strerror(status) \
|
154 |
|
|
printk( KERN_ERR "IP2: PCI error 0x%x \n", status );
|
155 |
|
|
# endif
|
156 |
|
|
|
157 |
|
|
#else /* 2.0.x and 2.1.x before 2.1.4 */
|
158 |
|
|
|
159 |
|
|
# define proc_register_dynamic(a,b) proc_register(a,b)
|
160 |
|
|
|
161 |
|
|
# define GET_USER(error,value,addr) \
|
162 |
|
|
do { \
|
163 |
|
|
error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \
|
164 |
|
|
if (error == 0) \
|
165 |
|
|
value = get_user(addr); \
|
166 |
|
|
} while (0)
|
167 |
|
|
|
168 |
|
|
# define COPY_FROM_USER(error,dest,src,size) \
|
169 |
|
|
do { \
|
170 |
|
|
error = verify_area (VERIFY_READ, (void *) src, size); \
|
171 |
|
|
if (error == 0) \
|
172 |
|
|
memcpy_fromfs (dest, src, size); \
|
173 |
|
|
} while (0)
|
174 |
|
|
|
175 |
|
|
# define PUT_USER(error,value,addr) \
|
176 |
|
|
do { \
|
177 |
|
|
error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \
|
178 |
|
|
if (error == 0) \
|
179 |
|
|
put_user (value, addr); \
|
180 |
|
|
} while (0)
|
181 |
|
|
|
182 |
|
|
# define COPY_TO_USER(error,dest,src,size) \
|
183 |
|
|
do { \
|
184 |
|
|
error = verify_area (VERIFY_WRITE, (void *) dest, size); \
|
185 |
|
|
if (error == 0) \
|
186 |
|
|
memcpy_tofs (dest, src, size); \
|
187 |
|
|
} while (0)
|
188 |
|
|
|
189 |
|
|
#endif
|
190 |
|
|
|
191 |
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
|
192 |
|
|
#define __init
|
193 |
|
|
#define __initfunc(a) a
|
194 |
|
|
#define __initdata
|
195 |
|
|
#define ioremap(a,b) vremap((a),(b))
|
196 |
|
|
#define iounmap(a) vfree((a))
|
197 |
|
|
#define SERIAL_TYPE_NORMAL 1
|
198 |
|
|
#define SERIAL_TYPE_CALLOUT 2
|
199 |
|
|
#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();}
|
200 |
|
|
#define signal_pending(a) ((a)->signal & ~(a)->blocked)
|
201 |
|
|
#define in_interrupt() intr_count
|
202 |
|
|
#endif
|
203 |
|
|
|
204 |
|
|
#include "./ip2/ip2types.h"
|
205 |
|
|
#include "./ip2/ip2trace.h"
|
206 |
|
|
#include "./ip2/ip2ioctl.h"
|
207 |
|
|
#include "./ip2/ip2.h"
|
208 |
|
|
#include "./ip2/i2ellis.h"
|
209 |
|
|
#include "./ip2/i2lib.h"
|
210 |
|
|
|
211 |
|
|
/*****************
|
212 |
|
|
* /proc/ip2mem *
|
213 |
|
|
*****************/
|
214 |
|
|
|
215 |
|
|
#include <linux/proc_fs.h>
|
216 |
|
|
|
217 |
|
|
static int ip2_read_procmem(char *, char **, off_t, int);
|
218 |
|
|
int ip2_read_proc(char *, char **, off_t, int, int *, void * );
|
219 |
|
|
|
220 |
|
|
/********************/
|
221 |
|
|
/* Type Definitions */
|
222 |
|
|
/********************/
|
223 |
|
|
|
224 |
|
|
/*************/
|
225 |
|
|
/* Constants */
|
226 |
|
|
/*************/
|
227 |
|
|
|
228 |
|
|
/* String constants to identify ourselves */
|
229 |
|
|
static char *pcName = "Computone IntelliPort Plus multiport driver";
|
230 |
|
|
static char *pcVersion = "1.2.14";
|
231 |
|
|
|
232 |
|
|
/* String constants for port names */
|
233 |
|
|
static char *pcDriver_name = "ip2";
|
234 |
|
|
#ifdef CONFIG_DEVFS_FS
|
235 |
|
|
static char *pcTty = "tts/F%d";
|
236 |
|
|
static char *pcCallout = "cua/F%d";
|
237 |
|
|
#else
|
238 |
|
|
static char *pcTty = "ttyF";
|
239 |
|
|
static char *pcCallout = "cuf";
|
240 |
|
|
#endif
|
241 |
|
|
static char *pcIpl = "ip2ipl";
|
242 |
|
|
|
243 |
|
|
/* Serial subtype definitions */
|
244 |
|
|
#define SERIAL_TYPE_NORMAL 1
|
245 |
|
|
#define SERIAL_TYPE_CALLOUT 2
|
246 |
|
|
|
247 |
|
|
// cheezy kludge or genius - you decide?
|
248 |
|
|
int ip2_loadmain(int *, int *, unsigned char *, int);
|
249 |
|
|
static unsigned char *Fip_firmware;
|
250 |
|
|
static int Fip_firmware_size;
|
251 |
|
|
|
252 |
|
|
/***********************/
|
253 |
|
|
/* Function Prototypes */
|
254 |
|
|
/***********************/
|
255 |
|
|
|
256 |
|
|
/* Global module entry functions */
|
257 |
|
|
#ifdef MODULE
|
258 |
|
|
int init_module(void);
|
259 |
|
|
void cleanup_module(void);
|
260 |
|
|
#endif
|
261 |
|
|
|
262 |
|
|
/* Private (static) functions */
|
263 |
|
|
static int ip2_open(PTTY, struct file *);
|
264 |
|
|
static void ip2_close(PTTY, struct file *);
|
265 |
|
|
static int ip2_write(PTTY, int, const unsigned char *, int);
|
266 |
|
|
static void ip2_putchar(PTTY, unsigned char);
|
267 |
|
|
static void ip2_flush_chars(PTTY);
|
268 |
|
|
static int ip2_write_room(PTTY);
|
269 |
|
|
static int ip2_chars_in_buf(PTTY);
|
270 |
|
|
static void ip2_flush_buffer(PTTY);
|
271 |
|
|
static int ip2_ioctl(PTTY, struct file *, UINT, ULONG);
|
272 |
|
|
static void ip2_set_termios(PTTY, struct termios *);
|
273 |
|
|
static void ip2_set_line_discipline(PTTY);
|
274 |
|
|
static void ip2_throttle(PTTY);
|
275 |
|
|
static void ip2_unthrottle(PTTY);
|
276 |
|
|
static void ip2_stop(PTTY);
|
277 |
|
|
static void ip2_start(PTTY);
|
278 |
|
|
static void ip2_hangup(PTTY);
|
279 |
|
|
|
280 |
|
|
static void set_irq(int, int);
|
281 |
|
|
static void ip2_interrupt_bh(i2eBordStrPtr pB);
|
282 |
|
|
static void ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs);
|
283 |
|
|
static void ip2_poll(unsigned long arg);
|
284 |
|
|
static inline void service_all_boards(void);
|
285 |
|
|
static inline void do_input(i2ChanStrPtr pCh);
|
286 |
|
|
static inline void do_status(i2ChanStrPtr pCh);
|
287 |
|
|
|
288 |
|
|
static void ip2_wait_until_sent(PTTY,int);
|
289 |
|
|
|
290 |
|
|
static void set_params (i2ChanStrPtr, struct termios *);
|
291 |
|
|
static int set_modem_info(i2ChanStrPtr, unsigned int, unsigned int *);
|
292 |
|
|
static int get_serial_info(i2ChanStrPtr, struct serial_struct *);
|
293 |
|
|
static int set_serial_info(i2ChanStrPtr, struct serial_struct *);
|
294 |
|
|
|
295 |
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
|
296 |
|
|
static int ip2_ipl_read(struct inode *, char *, size_t , loff_t *);
|
297 |
|
|
#else
|
298 |
|
|
static ssize_t ip2_ipl_read(struct file *, char *, size_t, loff_t *) ;
|
299 |
|
|
#endif
|
300 |
|
|
static ssize_t ip2_ipl_write(struct file *, const char *, size_t, loff_t *);
|
301 |
|
|
static int ip2_ipl_ioctl(struct inode *, struct file *, UINT, ULONG);
|
302 |
|
|
static int ip2_ipl_open(struct inode *, struct file *);
|
303 |
|
|
|
304 |
|
|
static int DumpTraceBuffer(char *, int);
|
305 |
|
|
static int DumpFifoBuffer( char *, int);
|
306 |
|
|
|
307 |
|
|
static void ip2_init_board(int);
|
308 |
|
|
static unsigned short find_eisa_board(int);
|
309 |
|
|
|
310 |
|
|
/***************/
|
311 |
|
|
/* Static Data */
|
312 |
|
|
/***************/
|
313 |
|
|
|
314 |
|
|
static struct tty_driver ip2_tty_driver;
|
315 |
|
|
static struct tty_driver ip2_callout_driver;
|
316 |
|
|
|
317 |
|
|
static int ref_count;
|
318 |
|
|
|
319 |
|
|
/* Here, then is a table of board pointers which the interrupt routine should
|
320 |
|
|
* scan through to determine who it must service.
|
321 |
|
|
*/
|
322 |
|
|
static unsigned short i2nBoards; // Number of boards here
|
323 |
|
|
|
324 |
|
|
static i2eBordStrPtr i2BoardPtrTable[IP2_MAX_BOARDS];
|
325 |
|
|
|
326 |
|
|
static i2ChanStrPtr DevTable[IP2_MAX_PORTS];
|
327 |
|
|
//DevTableMem just used to save addresses for kfree
|
328 |
|
|
static void *DevTableMem[IP2_MAX_BOARDS];
|
329 |
|
|
|
330 |
|
|
static struct tty_struct * TtyTable[IP2_MAX_PORTS];
|
331 |
|
|
static struct termios * Termios[IP2_MAX_PORTS];
|
332 |
|
|
static struct termios * TermiosLocked[IP2_MAX_PORTS];
|
333 |
|
|
|
334 |
|
|
/* This is the driver descriptor for the ip2ipl device, which is used to
|
335 |
|
|
* download the loadware to the boards.
|
336 |
|
|
*/
|
337 |
|
|
static struct file_operations ip2_ipl = {
|
338 |
|
|
owner: THIS_MODULE,
|
339 |
|
|
read: ip2_ipl_read,
|
340 |
|
|
write: ip2_ipl_write,
|
341 |
|
|
ioctl: ip2_ipl_ioctl,
|
342 |
|
|
open: ip2_ipl_open,
|
343 |
|
|
};
|
344 |
|
|
|
345 |
|
|
static unsigned long irq_counter = 0;
|
346 |
|
|
static unsigned long bh_counter = 0;
|
347 |
|
|
|
348 |
|
|
// Use immediate queue to service interrupts
|
349 |
|
|
#define USE_IQI
|
350 |
|
|
//#define USE_IQ // PCI&2.2 needs work
|
351 |
|
|
|
352 |
|
|
/* The timer_list entry for our poll routine. If interrupt operation is not
|
353 |
|
|
* selected, the board is serviced periodically to see if anything needs doing.
|
354 |
|
|
*/
|
355 |
|
|
#define POLL_TIMEOUT (jiffies + 1)
|
356 |
|
|
static struct timer_list PollTimer = { function: ip2_poll };
|
357 |
|
|
static char TimerOn;
|
358 |
|
|
|
359 |
|
|
#ifdef IP2DEBUG_TRACE
|
360 |
|
|
/* Trace (debug) buffer data */
|
361 |
|
|
#define TRACEMAX 1000
|
362 |
|
|
static unsigned long tracebuf[TRACEMAX];
|
363 |
|
|
static int tracestuff;
|
364 |
|
|
static int tracestrip;
|
365 |
|
|
static int tracewrap;
|
366 |
|
|
#endif
|
367 |
|
|
|
368 |
|
|
/**********/
|
369 |
|
|
/* Macros */
|
370 |
|
|
/**********/
|
371 |
|
|
|
372 |
|
|
#if defined(MODULE) && defined(IP2DEBUG_OPEN)
|
373 |
|
|
#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \
|
374 |
|
|
kdevname(tty->device),(pCh->flags),ref_count, \
|
375 |
|
|
tty->count,/*GET_USE_COUNT(module)*/0,s)
|
376 |
|
|
#else
|
377 |
|
|
#define DBG_CNT(s)
|
378 |
|
|
#endif
|
379 |
|
|
|
380 |
|
|
#define MIN(a,b) ( ( (a) < (b) ) ? (a) : (b) )
|
381 |
|
|
#define MAX(a,b) ( ( (a) > (b) ) ? (a) : (b) )
|
382 |
|
|
|
383 |
|
|
/********/
|
384 |
|
|
/* Code */
|
385 |
|
|
/********/
|
386 |
|
|
|
387 |
|
|
#include "./ip2/i2ellis.c" /* Extremely low-level interface services */
|
388 |
|
|
#include "./ip2/i2cmd.c" /* Standard loadware command definitions */
|
389 |
|
|
#include "./ip2/i2lib.c" /* High level interface services */
|
390 |
|
|
|
391 |
|
|
/* Configuration area for modprobe */
|
392 |
|
|
#ifdef MODULE
|
393 |
|
|
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
|
394 |
|
|
MODULE_AUTHOR("Doug McNash");
|
395 |
|
|
MODULE_DESCRIPTION("Computone IntelliPort Plus Driver");
|
396 |
|
|
# endif /* LINUX_VERSION */
|
397 |
|
|
#endif /* MODULE */
|
398 |
|
|
|
399 |
|
|
static int poll_only = 0;
|
400 |
|
|
|
401 |
|
|
static int Eisa_irq;
|
402 |
|
|
static int Eisa_slot;
|
403 |
|
|
|
404 |
|
|
static int iindx;
|
405 |
|
|
static char rirqs[IP2_MAX_BOARDS];
|
406 |
|
|
static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0};
|
407 |
|
|
|
408 |
|
|
// Some functions to keep track of what irq's we have
|
409 |
|
|
|
410 |
|
|
static int __init
|
411 |
|
|
is_valid_irq(int irq)
|
412 |
|
|
{
|
413 |
|
|
int *i = Valid_Irqs;
|
414 |
|
|
|
415 |
|
|
while ((*i != 0) && (*i != irq)) {
|
416 |
|
|
i++;
|
417 |
|
|
}
|
418 |
|
|
return (*i);
|
419 |
|
|
}
|
420 |
|
|
|
421 |
|
|
static void __init
|
422 |
|
|
mark_requested_irq( char irq )
|
423 |
|
|
{
|
424 |
|
|
rirqs[iindx++] = irq;
|
425 |
|
|
}
|
426 |
|
|
|
427 |
|
|
#ifdef MODULE
|
428 |
|
|
static int __init
|
429 |
|
|
clear_requested_irq( char irq )
|
430 |
|
|
{
|
431 |
|
|
int i;
|
432 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
433 |
|
|
if (rirqs[i] == irq) {
|
434 |
|
|
rirqs[i] = 0;
|
435 |
|
|
return 1;
|
436 |
|
|
}
|
437 |
|
|
}
|
438 |
|
|
return 0;
|
439 |
|
|
}
|
440 |
|
|
#endif
|
441 |
|
|
|
442 |
|
|
static int __init
|
443 |
|
|
have_requested_irq( char irq )
|
444 |
|
|
{
|
445 |
|
|
// array init to zeros so 0 irq will not be requested as a side effect
|
446 |
|
|
int i;
|
447 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
448 |
|
|
if (rirqs[i] == irq)
|
449 |
|
|
return 1;
|
450 |
|
|
}
|
451 |
|
|
return 0;
|
452 |
|
|
}
|
453 |
|
|
|
454 |
|
|
/******************************************************************************/
|
455 |
|
|
/* Function: init_module() */
|
456 |
|
|
/* Parameters: None */
|
457 |
|
|
/* Returns: Success (0) */
|
458 |
|
|
/* */
|
459 |
|
|
/* Description: */
|
460 |
|
|
/* This is a required entry point for an installable module. It simply calls */
|
461 |
|
|
/* the driver initialisation function and returns what it returns. */
|
462 |
|
|
/******************************************************************************/
|
463 |
|
|
#ifdef MODULE
|
464 |
|
|
int
|
465 |
|
|
init_module(void)
|
466 |
|
|
{
|
467 |
|
|
#ifdef IP2DEBUG_INIT
|
468 |
|
|
printk (KERN_DEBUG "Loading module ...\n" );
|
469 |
|
|
#endif
|
470 |
|
|
return 0;
|
471 |
|
|
}
|
472 |
|
|
#endif /* MODULE */
|
473 |
|
|
|
474 |
|
|
/******************************************************************************/
|
475 |
|
|
/* Function: cleanup_module() */
|
476 |
|
|
/* Parameters: None */
|
477 |
|
|
/* Returns: Nothing */
|
478 |
|
|
/* */
|
479 |
|
|
/* Description: */
|
480 |
|
|
/* This is a required entry point for an installable module. It has to return */
|
481 |
|
|
/* the device and the driver to a passive state. It should not be necessary */
|
482 |
|
|
/* to reset the board fully, especially as the loadware is downloaded */
|
483 |
|
|
/* externally rather than in the driver. We just want to disable the board */
|
484 |
|
|
/* and clear the loadware to a reset state. To allow this there has to be a */
|
485 |
|
|
/* way to detect whether the board has the loadware running at init time to */
|
486 |
|
|
/* handle subsequent installations of the driver. All memory allocated by the */
|
487 |
|
|
/* driver should be returned since it may be unloaded from memory. */
|
488 |
|
|
/******************************************************************************/
|
489 |
|
|
#ifdef MODULE
|
490 |
|
|
void
|
491 |
|
|
cleanup_module(void)
|
492 |
|
|
{
|
493 |
|
|
int err;
|
494 |
|
|
int i;
|
495 |
|
|
|
496 |
|
|
#ifdef IP2DEBUG_INIT
|
497 |
|
|
printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion );
|
498 |
|
|
#endif
|
499 |
|
|
/* Stop poll timer if we had one. */
|
500 |
|
|
if ( TimerOn ) {
|
501 |
|
|
del_timer ( &PollTimer );
|
502 |
|
|
TimerOn = 0;
|
503 |
|
|
}
|
504 |
|
|
|
505 |
|
|
/* Reset the boards we have. */
|
506 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
507 |
|
|
if ( i2BoardPtrTable[i] ) {
|
508 |
|
|
iiReset( i2BoardPtrTable[i] );
|
509 |
|
|
}
|
510 |
|
|
}
|
511 |
|
|
|
512 |
|
|
/* The following is done at most once, if any boards were installed. */
|
513 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
514 |
|
|
if ( i2BoardPtrTable[i] ) {
|
515 |
|
|
iiResetDelay( i2BoardPtrTable[i] );
|
516 |
|
|
/* free io addresses and Tibet */
|
517 |
|
|
release_region( ip2config.addr[i], 8 );
|
518 |
|
|
#ifdef CONFIG_DEVFS_FS
|
519 |
|
|
devfs_unregister (i2BoardPtrTable[i]->devfs_ipl_handle);
|
520 |
|
|
devfs_unregister (i2BoardPtrTable[i]->devfs_stat_handle);
|
521 |
|
|
#endif
|
522 |
|
|
}
|
523 |
|
|
/* Disable and remove interrupt handler. */
|
524 |
|
|
if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) {
|
525 |
|
|
free_irq ( ip2config.irq[i], (void *)&pcName);
|
526 |
|
|
clear_requested_irq( ip2config.irq[i]);
|
527 |
|
|
}
|
528 |
|
|
}
|
529 |
|
|
if ( ( err = tty_unregister_driver ( &ip2_tty_driver ) ) ) {
|
530 |
|
|
printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err);
|
531 |
|
|
}
|
532 |
|
|
if ( ( err = tty_unregister_driver ( &ip2_callout_driver ) ) ) {
|
533 |
|
|
printk(KERN_ERR "IP2: failed to unregister callout driver (%d)\n", err);
|
534 |
|
|
}
|
535 |
|
|
#ifdef CONFIG_DEVFS_FS
|
536 |
|
|
if ( ( err = devfs_unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) )
|
537 |
|
|
#else
|
538 |
|
|
if ( ( err = unregister_chrdev ( IP2_IPL_MAJOR, pcIpl ) ) )
|
539 |
|
|
#endif
|
540 |
|
|
{
|
541 |
|
|
printk(KERN_ERR "IP2: failed to unregister IPL driver (%d)\n", err);
|
542 |
|
|
}
|
543 |
|
|
remove_proc_entry("ip2mem", &proc_root);
|
544 |
|
|
|
545 |
|
|
// free memory
|
546 |
|
|
for (i = 0; i < IP2_MAX_BOARDS; i++) {
|
547 |
|
|
void *pB;
|
548 |
|
|
if ((pB = i2BoardPtrTable[i]) != 0 ) {
|
549 |
|
|
kfree ( pB );
|
550 |
|
|
i2BoardPtrTable[i] = NULL;
|
551 |
|
|
}
|
552 |
|
|
if ((DevTableMem[i]) != NULL ) {
|
553 |
|
|
kfree ( DevTableMem[i] );
|
554 |
|
|
DevTableMem[i] = NULL;
|
555 |
|
|
}
|
556 |
|
|
}
|
557 |
|
|
|
558 |
|
|
/* Cleanup the iiEllis subsystem. */
|
559 |
|
|
iiEllisCleanup();
|
560 |
|
|
#ifdef IP2DEBUG_INIT
|
561 |
|
|
printk (KERN_DEBUG "IP2 Unloaded\n" );
|
562 |
|
|
#endif
|
563 |
|
|
}
|
564 |
|
|
#endif /* MODULE */
|
565 |
|
|
|
566 |
|
|
/******************************************************************************/
|
567 |
|
|
/* Function: ip2_loadmain() */
|
568 |
|
|
/* Parameters: irq, io from command line of insmod et. al. */
|
569 |
|
|
/* pointer to fip firmware and firmware size for boards */
|
570 |
|
|
/* Returns: Success (0) */
|
571 |
|
|
/* */
|
572 |
|
|
/* Description: */
|
573 |
|
|
/* This was the required entry point for all drivers (now in ip2.c) */
|
574 |
|
|
/* It performs all */
|
575 |
|
|
/* initialisation of the devices and driver structures, and registers itself */
|
576 |
|
|
/* with the relevant kernel modules. */
|
577 |
|
|
/******************************************************************************/
|
578 |
|
|
/* SA_INTERRUPT- if set blocks all interrupts else only this line */
|
579 |
|
|
/* SA_SHIRQ - for shared irq PCI or maybe EISA only */
|
580 |
|
|
/* SA_RANDOM - can be source for cert. random number generators */
|
581 |
|
|
#define IP2_SA_FLAGS 0
|
582 |
|
|
|
583 |
|
|
int __init
|
584 |
|
|
ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
|
585 |
|
|
{
|
586 |
|
|
#ifdef CONFIG_DEVFS_FS
|
587 |
|
|
static devfs_handle_t devfs_handle;
|
588 |
|
|
int j, box;
|
589 |
|
|
#endif
|
590 |
|
|
int i;
|
591 |
|
|
int err;
|
592 |
|
|
int status = 0;
|
593 |
|
|
static int loaded;
|
594 |
|
|
i2eBordStrPtr pB = NULL;
|
595 |
|
|
int rc = -1;
|
596 |
|
|
|
597 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 );
|
598 |
|
|
|
599 |
|
|
/* process command line arguments to modprobe or
|
600 |
|
|
insmod i.e. iop & irqp */
|
601 |
|
|
/* irqp and iop should ALWAYS be specified now... But we check
|
602 |
|
|
them individually just to be sure, anyways... */
|
603 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
604 |
|
|
if (iop) {
|
605 |
|
|
ip2config.addr[i] = iop[i];
|
606 |
|
|
if (irqp) {
|
607 |
|
|
if( irqp[i] >= 0 ) {
|
608 |
|
|
ip2config.irq[i] = irqp[i];
|
609 |
|
|
} else {
|
610 |
|
|
ip2config.irq[i] = 0;
|
611 |
|
|
}
|
612 |
|
|
// This is a little bit of a hack. If poll_only=1 on command
|
613 |
|
|
// line back in ip2.c OR all IRQs on all specified boards are
|
614 |
|
|
// explicitly set to 0, then drop to poll only mode and override
|
615 |
|
|
// PCI or EISA interrupts. This superceeds the old hack of
|
616 |
|
|
// triggering if all interrupts were zero (like da default).
|
617 |
|
|
// Still a hack but less prone to random acts of terrorism.
|
618 |
|
|
//
|
619 |
|
|
// What we really should do, now that the IRQ default is set
|
620 |
|
|
// to -1, is to use 0 as a hard coded, do not probe.
|
621 |
|
|
//
|
622 |
|
|
// /\/\|=mhw=|\/\/
|
623 |
|
|
poll_only |= irqp[i];
|
624 |
|
|
}
|
625 |
|
|
}
|
626 |
|
|
}
|
627 |
|
|
poll_only = !poll_only;
|
628 |
|
|
|
629 |
|
|
Fip_firmware = firmware;
|
630 |
|
|
Fip_firmware_size = firmsize;
|
631 |
|
|
|
632 |
|
|
/* Announce our presence */
|
633 |
|
|
printk( KERN_INFO "%s version %s\n", pcName, pcVersion );
|
634 |
|
|
|
635 |
|
|
// ip2 can be unloaded and reloaded for no good reason
|
636 |
|
|
// we can't let that happen here or bad things happen
|
637 |
|
|
// second load hoses board but not system - fixme later
|
638 |
|
|
if (loaded) {
|
639 |
|
|
printk( KERN_INFO "Still loaded\n" );
|
640 |
|
|
return 0;
|
641 |
|
|
}
|
642 |
|
|
loaded++;
|
643 |
|
|
|
644 |
|
|
/* Initialise the iiEllis subsystem. */
|
645 |
|
|
iiEllisInit();
|
646 |
|
|
|
647 |
|
|
/* Initialize arrays. */
|
648 |
|
|
memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable );
|
649 |
|
|
memset( DevTable, 0, sizeof DevTable );
|
650 |
|
|
memset( TtyTable, 0, sizeof TtyTable );
|
651 |
|
|
memset( Termios, 0, sizeof Termios );
|
652 |
|
|
memset( TermiosLocked, 0, sizeof TermiosLocked );
|
653 |
|
|
|
654 |
|
|
/* Initialise all the boards we can find (up to the maximum). */
|
655 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
656 |
|
|
switch ( ip2config.addr[i] ) {
|
657 |
|
|
case 0: /* skip this slot even if card is present */
|
658 |
|
|
break;
|
659 |
|
|
default: /* ISA */
|
660 |
|
|
/* ISA address must be specified */
|
661 |
|
|
if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) {
|
662 |
|
|
printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n",
|
663 |
|
|
i, ip2config.addr[i] );
|
664 |
|
|
ip2config.addr[i] = 0;
|
665 |
|
|
} else {
|
666 |
|
|
ip2config.type[i] = ISA;
|
667 |
|
|
|
668 |
|
|
/* Check for valid irq argument, set for polling if invalid */
|
669 |
|
|
if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) {
|
670 |
|
|
printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]);
|
671 |
|
|
ip2config.irq[i] = 0;// 0 is polling and is valid in that sense
|
672 |
|
|
}
|
673 |
|
|
}
|
674 |
|
|
break;
|
675 |
|
|
case PCI:
|
676 |
|
|
#ifdef CONFIG_PCI
|
677 |
|
|
#if (LINUX_VERSION_CODE < 0x020163) /* 2.1.99 */
|
678 |
|
|
if (pcibios_present()) {
|
679 |
|
|
unsigned char pci_bus, pci_devfn;
|
680 |
|
|
int Pci_index = 0;
|
681 |
|
|
status = pcibios_find_device(PCI_VENDOR_ID_COMPUTONE,
|
682 |
|
|
PCI_DEVICE_ID_COMPUTONE_IP2EX, Pci_index,
|
683 |
|
|
&pci_bus, &pci_devfn);
|
684 |
|
|
if (status == 0) {
|
685 |
|
|
unsigned int addr;
|
686 |
|
|
unsigned char pci_irq;
|
687 |
|
|
|
688 |
|
|
ip2config.type[i] = PCI;
|
689 |
|
|
/*
|
690 |
|
|
* Update Pci_index, so that the next time we go
|
691 |
|
|
* searching for a PCI board we find a different
|
692 |
|
|
* one.
|
693 |
|
|
*/
|
694 |
|
|
++Pci_index;
|
695 |
|
|
|
696 |
|
|
pcibios_read_config_dword(pci_bus, pci_devfn,
|
697 |
|
|
PCI_BASE_ADDRESS_1, &addr);
|
698 |
|
|
if ( addr & 1 ) {
|
699 |
|
|
ip2config.addr[i]=(USHORT)(addr&0xfffe);
|
700 |
|
|
} else {
|
701 |
|
|
printk( KERN_ERR "IP2: PCI I/O address error\n");
|
702 |
|
|
}
|
703 |
|
|
pcibios_read_config_byte(pci_bus, pci_devfn,
|
704 |
|
|
PCI_INTERRUPT_LINE, &pci_irq);
|
705 |
|
|
|
706 |
|
|
// If the PCI BIOS assigned it, lets try and use it. If we
|
707 |
|
|
// can't acquire it or it screws up, deal with it then.
|
708 |
|
|
|
709 |
|
|
// if (!is_valid_irq(pci_irq)) {
|
710 |
|
|
// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
|
711 |
|
|
// pci_irq = 0;
|
712 |
|
|
// }
|
713 |
|
|
ip2config.irq[i] = pci_irq;
|
714 |
|
|
} else { // ann error
|
715 |
|
|
ip2config.addr[i] = 0;
|
716 |
|
|
if (status == PCIBIOS_DEVICE_NOT_FOUND) {
|
717 |
|
|
printk( KERN_ERR "IP2: PCI board %d not found\n", i );
|
718 |
|
|
} else {
|
719 |
|
|
pcibios_strerror(status);
|
720 |
|
|
}
|
721 |
|
|
}
|
722 |
|
|
}
|
723 |
|
|
#else /* LINUX_VERSION_CODE > 2.1.99 */
|
724 |
|
|
if (pci_present()) {
|
725 |
|
|
struct pci_dev *pci_dev_i = NULL;
|
726 |
|
|
pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE,
|
727 |
|
|
PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i);
|
728 |
|
|
if (pci_dev_i != NULL) {
|
729 |
|
|
unsigned int addr;
|
730 |
|
|
unsigned char pci_irq;
|
731 |
|
|
|
732 |
|
|
ip2config.type[i] = PCI;
|
733 |
|
|
status =
|
734 |
|
|
pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr);
|
735 |
|
|
if ( addr & 1 ) {
|
736 |
|
|
ip2config.addr[i]=(USHORT)(addr&0xfffe);
|
737 |
|
|
} else {
|
738 |
|
|
printk( KERN_ERR "IP2: PCI I/O address error\n");
|
739 |
|
|
}
|
740 |
|
|
status =
|
741 |
|
|
pci_read_config_byte(pci_dev_i, PCI_INTERRUPT_LINE, &pci_irq);
|
742 |
|
|
|
743 |
|
|
// If the PCI BIOS assigned it, lets try and use it. If we
|
744 |
|
|
// can't acquire it or it screws up, deal with it then.
|
745 |
|
|
|
746 |
|
|
// if (!is_valid_irq(pci_irq)) {
|
747 |
|
|
// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq);
|
748 |
|
|
// pci_irq = 0;
|
749 |
|
|
// }
|
750 |
|
|
ip2config.irq[i] = pci_irq;
|
751 |
|
|
} else { // ann error
|
752 |
|
|
ip2config.addr[i] = 0;
|
753 |
|
|
if (status == PCIBIOS_DEVICE_NOT_FOUND) {
|
754 |
|
|
printk( KERN_ERR "IP2: PCI board %d not found\n", i );
|
755 |
|
|
} else {
|
756 |
|
|
pcibios_strerror(status);
|
757 |
|
|
}
|
758 |
|
|
}
|
759 |
|
|
}
|
760 |
|
|
#endif /* ! 2_0_X */
|
761 |
|
|
#else
|
762 |
|
|
printk( KERN_ERR "IP2: PCI card specified but PCI support not\n");
|
763 |
|
|
printk( KERN_ERR "IP2: configured in this kernel.\n");
|
764 |
|
|
printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n");
|
765 |
|
|
#endif /* CONFIG_PCI */
|
766 |
|
|
break;
|
767 |
|
|
case EISA:
|
768 |
|
|
if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) {
|
769 |
|
|
/* Eisa_irq set as side effect, boo */
|
770 |
|
|
ip2config.type[i] = EISA;
|
771 |
|
|
}
|
772 |
|
|
ip2config.irq[i] = Eisa_irq;
|
773 |
|
|
break;
|
774 |
|
|
} /* switch */
|
775 |
|
|
} /* for */
|
776 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
777 |
|
|
if ( ip2config.addr[i] ) {
|
778 |
|
|
pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL);
|
779 |
|
|
if ( pB != NULL ) {
|
780 |
|
|
i2BoardPtrTable[i] = pB;
|
781 |
|
|
memset( pB, 0, sizeof(i2eBordStr) );
|
782 |
|
|
iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer );
|
783 |
|
|
iiReset( pB );
|
784 |
|
|
} else {
|
785 |
|
|
printk(KERN_ERR "IP2: board memory allocation error\n");
|
786 |
|
|
}
|
787 |
|
|
}
|
788 |
|
|
}
|
789 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
790 |
|
|
if ( ( pB = i2BoardPtrTable[i] ) != NULL ) {
|
791 |
|
|
iiResetDelay( pB );
|
792 |
|
|
break;
|
793 |
|
|
}
|
794 |
|
|
}
|
795 |
|
|
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
796 |
|
|
if ( i2BoardPtrTable[i] != NULL ) {
|
797 |
|
|
ip2_init_board( i );
|
798 |
|
|
}
|
799 |
|
|
}
|
800 |
|
|
|
801 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 );
|
802 |
|
|
|
803 |
|
|
/* Zero out the normal tty device structure. */
|
804 |
|
|
memset ( &ip2_tty_driver, 0, sizeof ip2_tty_driver );
|
805 |
|
|
|
806 |
|
|
/* Initialise the relevant fields. */
|
807 |
|
|
ip2_tty_driver.magic = TTY_DRIVER_MAGIC;
|
808 |
|
|
ip2_tty_driver.name = pcTty;
|
809 |
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
|
810 |
|
|
ip2_tty_driver.driver_name = pcDriver_name;
|
811 |
|
|
ip2_tty_driver.read_proc = ip2_read_proc;
|
812 |
|
|
#endif
|
813 |
|
|
ip2_tty_driver.major = IP2_TTY_MAJOR;
|
814 |
|
|
ip2_tty_driver.minor_start = 0;
|
815 |
|
|
ip2_tty_driver.num = IP2_MAX_PORTS;
|
816 |
|
|
ip2_tty_driver.type = TTY_DRIVER_TYPE_SERIAL;
|
817 |
|
|
ip2_tty_driver.subtype = SERIAL_TYPE_NORMAL;
|
818 |
|
|
ip2_tty_driver.init_termios = tty_std_termios;
|
819 |
|
|
ip2_tty_driver.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
|
820 |
|
|
#ifdef CONFIG_DEVFS_FS
|
821 |
|
|
ip2_tty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
|
822 |
|
|
#else
|
823 |
|
|
ip2_tty_driver.flags = TTY_DRIVER_REAL_RAW;
|
824 |
|
|
#endif
|
825 |
|
|
ip2_tty_driver.refcount = &ref_count;
|
826 |
|
|
ip2_tty_driver.table = TtyTable;
|
827 |
|
|
ip2_tty_driver.termios = Termios;
|
828 |
|
|
ip2_tty_driver.termios_locked = TermiosLocked;
|
829 |
|
|
|
830 |
|
|
/* Setup the pointers to the implemented functions. */
|
831 |
|
|
ip2_tty_driver.open = ip2_open;
|
832 |
|
|
ip2_tty_driver.close = ip2_close;
|
833 |
|
|
ip2_tty_driver.write = ip2_write;
|
834 |
|
|
ip2_tty_driver.put_char = ip2_putchar;
|
835 |
|
|
ip2_tty_driver.flush_chars = ip2_flush_chars;
|
836 |
|
|
ip2_tty_driver.write_room = ip2_write_room;
|
837 |
|
|
ip2_tty_driver.chars_in_buffer = ip2_chars_in_buf;
|
838 |
|
|
ip2_tty_driver.flush_buffer = ip2_flush_buffer;
|
839 |
|
|
ip2_tty_driver.ioctl = ip2_ioctl;
|
840 |
|
|
ip2_tty_driver.throttle = ip2_throttle;
|
841 |
|
|
ip2_tty_driver.unthrottle = ip2_unthrottle;
|
842 |
|
|
ip2_tty_driver.set_termios = ip2_set_termios;
|
843 |
|
|
ip2_tty_driver.set_ldisc = ip2_set_line_discipline;
|
844 |
|
|
ip2_tty_driver.stop = ip2_stop;
|
845 |
|
|
ip2_tty_driver.start = ip2_start;
|
846 |
|
|
ip2_tty_driver.hangup = ip2_hangup;
|
847 |
|
|
|
848 |
|
|
/* Initialise the callout driver structure from the tty driver, and
|
849 |
|
|
* make the needed adjustments.
|
850 |
|
|
*/
|
851 |
|
|
ip2_callout_driver = ip2_tty_driver;
|
852 |
|
|
ip2_callout_driver.name = pcCallout;
|
853 |
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
|
854 |
|
|
ip2_callout_driver.driver_name = pcDriver_name;
|
855 |
|
|
ip2_callout_driver.read_proc = NULL;
|
856 |
|
|
#endif
|
857 |
|
|
ip2_callout_driver.major = IP2_CALLOUT_MAJOR;
|
858 |
|
|
ip2_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
|
859 |
|
|
|
860 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 );
|
861 |
|
|
|
862 |
|
|
/* Register the tty devices. */
|
863 |
|
|
if ( ( err = tty_register_driver ( &ip2_tty_driver ) ) ) {
|
864 |
|
|
printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err);
|
865 |
|
|
} else
|
866 |
|
|
if ( ( err = tty_register_driver ( &ip2_callout_driver ) ) ) {
|
867 |
|
|
printk(KERN_ERR "IP2: failed to register callout driver (%d)\n", err);
|
868 |
|
|
} else
|
869 |
|
|
/* Register the IPL driver. */
|
870 |
|
|
#ifdef CONFIG_DEVFS_FS
|
871 |
|
|
if (( err = devfs_register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl )))
|
872 |
|
|
#else
|
873 |
|
|
if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) )
|
874 |
|
|
#endif
|
875 |
|
|
{
|
876 |
|
|
printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err );
|
877 |
|
|
} else
|
878 |
|
|
/* Register the read_procmem thing */
|
879 |
|
|
if (!create_proc_info_entry("ip2mem",0,&proc_root,ip2_read_procmem)) {
|
880 |
|
|
printk(KERN_ERR "IP2: failed to register read_procmem\n");
|
881 |
|
|
} else {
|
882 |
|
|
|
883 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 );
|
884 |
|
|
/* Register the interrupt handler or poll handler, depending upon the
|
885 |
|
|
* specified interrupt.
|
886 |
|
|
*/
|
887 |
|
|
#ifdef CONFIG_DEVFS_FS
|
888 |
|
|
if (!devfs_handle)
|
889 |
|
|
devfs_handle = devfs_mk_dir (NULL, "ip2", NULL);
|
890 |
|
|
#endif
|
891 |
|
|
|
892 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
893 |
|
|
#ifdef CONFIG_DEVFS_FS
|
894 |
|
|
char name[16];
|
895 |
|
|
#endif
|
896 |
|
|
|
897 |
|
|
if ( 0 == ip2config.addr[i] ) {
|
898 |
|
|
continue;
|
899 |
|
|
}
|
900 |
|
|
|
901 |
|
|
#ifdef CONFIG_DEVFS_FS
|
902 |
|
|
if ( NULL != ( pB = i2BoardPtrTable[i] ) ) {
|
903 |
|
|
sprintf( name, "ipl%d", i );
|
904 |
|
|
pB->devfs_ipl_handle =
|
905 |
|
|
devfs_register (devfs_handle, name,
|
906 |
|
|
DEVFS_FL_DEFAULT,
|
907 |
|
|
IP2_IPL_MAJOR, 4 * i,
|
908 |
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
|
909 |
|
|
&ip2_ipl, NULL);
|
910 |
|
|
|
911 |
|
|
sprintf( name, "stat%d", i );
|
912 |
|
|
pB->devfs_stat_handle =
|
913 |
|
|
devfs_register (devfs_handle, name,
|
914 |
|
|
DEVFS_FL_DEFAULT,
|
915 |
|
|
IP2_IPL_MAJOR, 4 * i + 1,
|
916 |
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR,
|
917 |
|
|
&ip2_ipl, NULL);
|
918 |
|
|
|
919 |
|
|
for ( box = 0; box < ABS_MAX_BOXES; ++box )
|
920 |
|
|
{
|
921 |
|
|
for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
|
922 |
|
|
{
|
923 |
|
|
if ( pB->i2eChannelMap[box] & (1 << j) )
|
924 |
|
|
{
|
925 |
|
|
tty_register_devfs(&ip2_tty_driver,
|
926 |
|
|
0, j + ABS_BIGGEST_BOX *
|
927 |
|
|
(box+i*ABS_MAX_BOXES));
|
928 |
|
|
tty_register_devfs(&ip2_callout_driver,
|
929 |
|
|
0, j + ABS_BIGGEST_BOX *
|
930 |
|
|
(box+i*ABS_MAX_BOXES));
|
931 |
|
|
}
|
932 |
|
|
}
|
933 |
|
|
}
|
934 |
|
|
}
|
935 |
|
|
#endif
|
936 |
|
|
|
937 |
|
|
if (poll_only) {
|
938 |
|
|
// Poll only forces driver to only use polling and
|
939 |
|
|
// to ignore the probed PCI or EISA interrupts.
|
940 |
|
|
ip2config.irq[i] = CIR_POLL;
|
941 |
|
|
}
|
942 |
|
|
if ( ip2config.irq[i] == CIR_POLL ) {
|
943 |
|
|
retry:
|
944 |
|
|
if (!TimerOn) {
|
945 |
|
|
PollTimer.expires = POLL_TIMEOUT;
|
946 |
|
|
add_timer ( &PollTimer );
|
947 |
|
|
TimerOn = 1;
|
948 |
|
|
printk( KERN_INFO "IP2: polling\n");
|
949 |
|
|
}
|
950 |
|
|
} else {
|
951 |
|
|
if (have_requested_irq(ip2config.irq[i]))
|
952 |
|
|
continue;
|
953 |
|
|
rc = request_irq( ip2config.irq[i], ip2_interrupt,
|
954 |
|
|
IP2_SA_FLAGS | (ip2config.type[i] == PCI ? SA_SHIRQ : 0),
|
955 |
|
|
pcName, (void *)&pcName);
|
956 |
|
|
if (rc) {
|
957 |
|
|
printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc);
|
958 |
|
|
ip2config.irq[i] = CIR_POLL;
|
959 |
|
|
printk( KERN_INFO "IP2: Polling %ld/sec.\n",
|
960 |
|
|
(POLL_TIMEOUT - jiffies));
|
961 |
|
|
goto retry;
|
962 |
|
|
}
|
963 |
|
|
mark_requested_irq(ip2config.irq[i]);
|
964 |
|
|
/* Initialise the interrupt handler bottom half (aka slih). */
|
965 |
|
|
}
|
966 |
|
|
}
|
967 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
968 |
|
|
if ( i2BoardPtrTable[i] ) {
|
969 |
|
|
set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */
|
970 |
|
|
}
|
971 |
|
|
}
|
972 |
|
|
}
|
973 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 );
|
974 |
|
|
|
975 |
|
|
return 0;
|
976 |
|
|
}
|
977 |
|
|
|
978 |
|
|
/******************************************************************************/
|
979 |
|
|
/* Function: ip2_init_board() */
|
980 |
|
|
/* Parameters: Index of board in configuration structure */
|
981 |
|
|
/* Returns: Success (0) */
|
982 |
|
|
/* */
|
983 |
|
|
/* Description: */
|
984 |
|
|
/* This function initializes the specified board. The loadware is copied to */
|
985 |
|
|
/* the board, the channel structures are initialized, and the board details */
|
986 |
|
|
/* are reported on the console. */
|
987 |
|
|
/******************************************************************************/
|
988 |
|
|
static void __init
|
989 |
|
|
ip2_init_board( int boardnum )
|
990 |
|
|
{
|
991 |
|
|
int i;
|
992 |
|
|
int nports = 0, nboxes = 0;
|
993 |
|
|
i2ChanStrPtr pCh;
|
994 |
|
|
i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
|
995 |
|
|
|
996 |
|
|
if ( !iiInitialize ( pB ) ) {
|
997 |
|
|
printk ( KERN_ERR "IP2: Failed to initialize board at 0x%x, error %d\n",
|
998 |
|
|
pB->i2eBase, pB->i2eError );
|
999 |
|
|
goto err_initialize;
|
1000 |
|
|
}
|
1001 |
|
|
printk(KERN_INFO "IP2: Board %d: addr=0x%x irq=%d\n", boardnum + 1,
|
1002 |
|
|
ip2config.addr[boardnum], ip2config.irq[boardnum] );
|
1003 |
|
|
|
1004 |
|
|
if (!request_region( ip2config.addr[boardnum], 8, pcName )) {
|
1005 |
|
|
printk(KERN_ERR "IP2: bad addr=0x%x\n", ip2config.addr[boardnum]);
|
1006 |
|
|
goto err_initialize;
|
1007 |
|
|
}
|
1008 |
|
|
|
1009 |
|
|
if ( iiDownloadAll ( pB, (loadHdrStrPtr)Fip_firmware, 1, Fip_firmware_size )
|
1010 |
|
|
!= II_DOWN_GOOD ) {
|
1011 |
|
|
printk ( KERN_ERR "IP2: failed to download loadware\n" );
|
1012 |
|
|
goto err_release_region;
|
1013 |
|
|
} else {
|
1014 |
|
|
printk ( KERN_INFO "IP2: fv=%d.%d.%d lv=%d.%d.%d\n",
|
1015 |
|
|
pB->i2ePom.e.porVersion,
|
1016 |
|
|
pB->i2ePom.e.porRevision,
|
1017 |
|
|
pB->i2ePom.e.porSubRev, pB->i2eLVersion,
|
1018 |
|
|
pB->i2eLRevision, pB->i2eLSub );
|
1019 |
|
|
}
|
1020 |
|
|
|
1021 |
|
|
switch ( pB->i2ePom.e.porID & ~POR_ID_RESERVED ) {
|
1022 |
|
|
|
1023 |
|
|
default:
|
1024 |
|
|
printk( KERN_ERR "IP2: Unknown board type, ID = %x\n",
|
1025 |
|
|
pB->i2ePom.e.porID );
|
1026 |
|
|
nports = 0;
|
1027 |
|
|
goto err_release_region;
|
1028 |
|
|
break;
|
1029 |
|
|
|
1030 |
|
|
case POR_ID_II_4: /* IntelliPort-II, ISA-4 (4xRJ45) */
|
1031 |
|
|
printk ( KERN_INFO "IP2: ISA-4\n" );
|
1032 |
|
|
nports = 4;
|
1033 |
|
|
break;
|
1034 |
|
|
|
1035 |
|
|
case POR_ID_II_8: /* IntelliPort-II, 8-port using standard brick. */
|
1036 |
|
|
printk ( KERN_INFO "IP2: ISA-8 std\n" );
|
1037 |
|
|
nports = 8;
|
1038 |
|
|
break;
|
1039 |
|
|
|
1040 |
|
|
case POR_ID_II_8R: /* IntelliPort-II, 8-port using RJ11's (no CTS) */
|
1041 |
|
|
printk ( KERN_INFO "IP2: ISA-8 RJ11\n" );
|
1042 |
|
|
nports = 8;
|
1043 |
|
|
break;
|
1044 |
|
|
|
1045 |
|
|
case POR_ID_FIIEX: /* IntelliPort IIEX */
|
1046 |
|
|
{
|
1047 |
|
|
int portnum = IP2_PORTS_PER_BOARD * boardnum;
|
1048 |
|
|
int box;
|
1049 |
|
|
|
1050 |
|
|
for( box = 0; box < ABS_MAX_BOXES; ++box ) {
|
1051 |
|
|
if ( pB->i2eChannelMap[box] != 0 ) {
|
1052 |
|
|
++nboxes;
|
1053 |
|
|
}
|
1054 |
|
|
for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
|
1055 |
|
|
if ( pB->i2eChannelMap[box] & 1<< i ) {
|
1056 |
|
|
++nports;
|
1057 |
|
|
}
|
1058 |
|
|
}
|
1059 |
|
|
}
|
1060 |
|
|
DevTableMem[boardnum] = pCh =
|
1061 |
|
|
kmalloc( sizeof(i2ChanStr) * nports, GFP_KERNEL );
|
1062 |
|
|
if ( !pCh ) {
|
1063 |
|
|
printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
|
1064 |
|
|
goto err_release_region;
|
1065 |
|
|
}
|
1066 |
|
|
if ( !i2InitChannels( pB, nports, pCh ) ) {
|
1067 |
|
|
printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
|
1068 |
|
|
kfree ( pCh );
|
1069 |
|
|
goto err_release_region;
|
1070 |
|
|
}
|
1071 |
|
|
pB->i2eChannelPtr = &DevTable[portnum];
|
1072 |
|
|
pB->i2eChannelCnt = ABS_MOST_PORTS;
|
1073 |
|
|
|
1074 |
|
|
for( box = 0; box < ABS_MAX_BOXES; ++box, portnum += ABS_BIGGEST_BOX ) {
|
1075 |
|
|
for( i = 0; i < ABS_BIGGEST_BOX; ++i ) {
|
1076 |
|
|
if ( pB->i2eChannelMap[box] & (1 << i) ) {
|
1077 |
|
|
DevTable[portnum + i] = pCh;
|
1078 |
|
|
pCh->port_index = portnum + i;
|
1079 |
|
|
pCh++;
|
1080 |
|
|
}
|
1081 |
|
|
}
|
1082 |
|
|
}
|
1083 |
|
|
printk(KERN_INFO "IP2: EX box=%d ports=%d %d bit\n",
|
1084 |
|
|
nboxes, nports, pB->i2eDataWidth16 ? 16 : 8 );
|
1085 |
|
|
}
|
1086 |
|
|
goto ex_exit;
|
1087 |
|
|
}
|
1088 |
|
|
DevTableMem[boardnum] = pCh =
|
1089 |
|
|
kmalloc ( sizeof (i2ChanStr) * nports, GFP_KERNEL );
|
1090 |
|
|
if ( !pCh ) {
|
1091 |
|
|
printk ( KERN_ERR "IP2: (i2_init_channel:) Out of memory.\n");
|
1092 |
|
|
goto err_release_region;
|
1093 |
|
|
}
|
1094 |
|
|
pB->i2eChannelPtr = pCh;
|
1095 |
|
|
pB->i2eChannelCnt = nports;
|
1096 |
|
|
if ( !i2InitChannels( pB, nports, pCh ) ) {
|
1097 |
|
|
printk(KERN_ERR "IP2: i2InitChannels failed: %d\n",pB->i2eError);
|
1098 |
|
|
kfree ( pCh );
|
1099 |
|
|
goto err_release_region;
|
1100 |
|
|
}
|
1101 |
|
|
pB->i2eChannelPtr = &DevTable[IP2_PORTS_PER_BOARD * boardnum];
|
1102 |
|
|
|
1103 |
|
|
for( i = 0; i < pB->i2eChannelCnt; ++i ) {
|
1104 |
|
|
DevTable[IP2_PORTS_PER_BOARD * boardnum + i] = pCh;
|
1105 |
|
|
pCh->port_index = (IP2_PORTS_PER_BOARD * boardnum) + i;
|
1106 |
|
|
pCh++;
|
1107 |
|
|
}
|
1108 |
|
|
ex_exit:
|
1109 |
|
|
pB->tqueue_interrupt.routine = (void(*)(void*)) ip2_interrupt_bh;
|
1110 |
|
|
pB->tqueue_interrupt.data = pB;
|
1111 |
|
|
return;
|
1112 |
|
|
|
1113 |
|
|
err_release_region:
|
1114 |
|
|
release_region(ip2config.addr[boardnum], 8);
|
1115 |
|
|
err_initialize:
|
1116 |
|
|
kfree ( pB );
|
1117 |
|
|
i2BoardPtrTable[boardnum] = NULL;
|
1118 |
|
|
return;
|
1119 |
|
|
}
|
1120 |
|
|
|
1121 |
|
|
/******************************************************************************/
|
1122 |
|
|
/* Function: find_eisa_board ( int start_slot ) */
|
1123 |
|
|
/* Parameters: First slot to check */
|
1124 |
|
|
/* Returns: Address of EISA IntelliPort II controller */
|
1125 |
|
|
/* */
|
1126 |
|
|
/* Description: */
|
1127 |
|
|
/* This function searches for an EISA IntelliPort controller, starting */
|
1128 |
|
|
/* from the specified slot number. If the motherboard is not identified as an */
|
1129 |
|
|
/* EISA motherboard, or no valid board ID is selected it returns 0. Otherwise */
|
1130 |
|
|
/* it returns the base address of the controller. */
|
1131 |
|
|
/******************************************************************************/
|
1132 |
|
|
static unsigned short __init
|
1133 |
|
|
find_eisa_board( int start_slot )
|
1134 |
|
|
{
|
1135 |
|
|
int i, j;
|
1136 |
|
|
unsigned int idm = 0;
|
1137 |
|
|
unsigned int idp = 0;
|
1138 |
|
|
unsigned int base = 0;
|
1139 |
|
|
unsigned int value;
|
1140 |
|
|
int setup_address;
|
1141 |
|
|
int setup_irq;
|
1142 |
|
|
int ismine = 0;
|
1143 |
|
|
|
1144 |
|
|
/*
|
1145 |
|
|
* First a check for an EISA motherboard, which we do by comparing the
|
1146 |
|
|
* EISA ID registers for the system board and the first couple of slots.
|
1147 |
|
|
* No slot ID should match the system board ID, but on an ISA or PCI
|
1148 |
|
|
* machine the odds are that an empty bus will return similar values for
|
1149 |
|
|
* each slot.
|
1150 |
|
|
*/
|
1151 |
|
|
i = 0x0c80;
|
1152 |
|
|
value = (inb(i) << 24) + (inb(i+1) << 16) + (inb(i+2) << 8) + inb(i+3);
|
1153 |
|
|
for( i = 0x1c80; i <= 0x4c80; i += 0x1000 ) {
|
1154 |
|
|
j = (inb(i)<<24)+(inb(i+1)<<16)+(inb(i+2)<<8)+inb(i+3);
|
1155 |
|
|
if ( value == j )
|
1156 |
|
|
return 0;
|
1157 |
|
|
}
|
1158 |
|
|
|
1159 |
|
|
/*
|
1160 |
|
|
* OK, so we are inclined to believe that this is an EISA machine. Find
|
1161 |
|
|
* an IntelliPort controller.
|
1162 |
|
|
*/
|
1163 |
|
|
for( i = start_slot; i < 16; i++ ) {
|
1164 |
|
|
base = i << 12;
|
1165 |
|
|
idm = (inb(base + 0xc80) << 8) | (inb(base + 0xc81) & 0xff);
|
1166 |
|
|
idp = (inb(base + 0xc82) << 8) | (inb(base + 0xc83) & 0xff);
|
1167 |
|
|
ismine = 0;
|
1168 |
|
|
if ( idm == 0x0e8e ) {
|
1169 |
|
|
if ( idp == 0x0281 || idp == 0x0218 ) {
|
1170 |
|
|
ismine = 1;
|
1171 |
|
|
} else if ( idp == 0x0282 || idp == 0x0283 ) {
|
1172 |
|
|
ismine = 3; /* Can do edge-trigger */
|
1173 |
|
|
}
|
1174 |
|
|
if ( ismine ) {
|
1175 |
|
|
Eisa_slot = i;
|
1176 |
|
|
break;
|
1177 |
|
|
}
|
1178 |
|
|
}
|
1179 |
|
|
}
|
1180 |
|
|
if ( !ismine )
|
1181 |
|
|
return 0;
|
1182 |
|
|
|
1183 |
|
|
/* It's some sort of EISA card, but at what address is it configured? */
|
1184 |
|
|
|
1185 |
|
|
setup_address = base + 0xc88;
|
1186 |
|
|
value = inb(base + 0xc86);
|
1187 |
|
|
setup_irq = (value & 8) ? Valid_Irqs[value & 7] : 0;
|
1188 |
|
|
|
1189 |
|
|
if ( (ismine & 2) && !(value & 0x10) ) {
|
1190 |
|
|
ismine = 1; /* Could be edging, but not */
|
1191 |
|
|
}
|
1192 |
|
|
|
1193 |
|
|
if ( Eisa_irq == 0 ) {
|
1194 |
|
|
Eisa_irq = setup_irq;
|
1195 |
|
|
} else if ( Eisa_irq != setup_irq ) {
|
1196 |
|
|
printk ( KERN_ERR "IP2: EISA irq mismatch between EISA controllers\n" );
|
1197 |
|
|
}
|
1198 |
|
|
|
1199 |
|
|
#ifdef IP2DEBUG_INIT
|
1200 |
|
|
printk(KERN_DEBUG "Computone EISA board in slot %d, I.D. 0x%x%x, Address 0x%x",
|
1201 |
|
|
base >> 12, idm, idp, setup_address);
|
1202 |
|
|
if ( Eisa_irq ) {
|
1203 |
|
|
printk(KERN_DEBUG ", Interrupt %d %s\n",
|
1204 |
|
|
setup_irq, (ismine & 2) ? "(edge)" : "(level)");
|
1205 |
|
|
} else {
|
1206 |
|
|
printk(KERN_DEBUG ", (polled)\n");
|
1207 |
|
|
}
|
1208 |
|
|
#endif
|
1209 |
|
|
return setup_address;
|
1210 |
|
|
}
|
1211 |
|
|
|
1212 |
|
|
/******************************************************************************/
|
1213 |
|
|
/* Function: set_irq() */
|
1214 |
|
|
/* Parameters: index to board in board table */
|
1215 |
|
|
/* IRQ to use */
|
1216 |
|
|
/* Returns: Success (0) */
|
1217 |
|
|
/* */
|
1218 |
|
|
/* Description: */
|
1219 |
|
|
/******************************************************************************/
|
1220 |
|
|
static void
|
1221 |
|
|
set_irq( int boardnum, int boardIrq )
|
1222 |
|
|
{
|
1223 |
|
|
unsigned char tempCommand[16];
|
1224 |
|
|
i2eBordStrPtr pB = i2BoardPtrTable[boardnum];
|
1225 |
|
|
unsigned long flags;
|
1226 |
|
|
|
1227 |
|
|
/*
|
1228 |
|
|
* Notify the boards they may generate interrupts. This is done by
|
1229 |
|
|
* sending an in-line command to channel 0 on each board. This is why
|
1230 |
|
|
* the channels have to be defined already. For each board, if the
|
1231 |
|
|
* interrupt has never been defined, we must do so NOW, directly, since
|
1232 |
|
|
* board will not send flow control or even give an interrupt until this
|
1233 |
|
|
* is done. If polling we must send 0 as the interrupt parameter.
|
1234 |
|
|
*/
|
1235 |
|
|
|
1236 |
|
|
// We will get an interrupt here at the end of this function
|
1237 |
|
|
|
1238 |
|
|
iiDisableMailIrq(pB);
|
1239 |
|
|
|
1240 |
|
|
/* We build up the entire packet header. */
|
1241 |
|
|
CHANNEL_OF(tempCommand) = 0;
|
1242 |
|
|
PTYPE_OF(tempCommand) = PTYPE_INLINE;
|
1243 |
|
|
CMD_COUNT_OF(tempCommand) = 2;
|
1244 |
|
|
(CMD_OF(tempCommand))[0] = CMDVALUE_IRQ;
|
1245 |
|
|
(CMD_OF(tempCommand))[1] = boardIrq;
|
1246 |
|
|
/*
|
1247 |
|
|
* Write to FIFO; don't bother to adjust fifo capacity for this, since
|
1248 |
|
|
* board will respond almost immediately after SendMail hit.
|
1249 |
|
|
*/
|
1250 |
|
|
WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags);
|
1251 |
|
|
iiWriteBuf(pB, tempCommand, 4);
|
1252 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags);
|
1253 |
|
|
pB->i2eUsingIrq = boardIrq;
|
1254 |
|
|
pB->i2eOutMailWaiting |= MB_OUT_STUFFED;
|
1255 |
|
|
|
1256 |
|
|
/* Need to update number of boards before you enable mailbox int */
|
1257 |
|
|
++i2nBoards;
|
1258 |
|
|
|
1259 |
|
|
CHANNEL_OF(tempCommand) = 0;
|
1260 |
|
|
PTYPE_OF(tempCommand) = PTYPE_BYPASS;
|
1261 |
|
|
CMD_COUNT_OF(tempCommand) = 6;
|
1262 |
|
|
(CMD_OF(tempCommand))[0] = 88; // SILO
|
1263 |
|
|
(CMD_OF(tempCommand))[1] = 64; // chars
|
1264 |
|
|
(CMD_OF(tempCommand))[2] = 32; // ms
|
1265 |
|
|
|
1266 |
|
|
(CMD_OF(tempCommand))[3] = 28; // MAX_BLOCK
|
1267 |
|
|
(CMD_OF(tempCommand))[4] = 64; // chars
|
1268 |
|
|
|
1269 |
|
|
(CMD_OF(tempCommand))[5] = 87; // HW_TEST
|
1270 |
|
|
WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags);
|
1271 |
|
|
iiWriteBuf(pB, tempCommand, 8);
|
1272 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags);
|
1273 |
|
|
|
1274 |
|
|
CHANNEL_OF(tempCommand) = 0;
|
1275 |
|
|
PTYPE_OF(tempCommand) = PTYPE_BYPASS;
|
1276 |
|
|
CMD_COUNT_OF(tempCommand) = 1;
|
1277 |
|
|
(CMD_OF(tempCommand))[0] = 84; /* get BOX_IDS */
|
1278 |
|
|
iiWriteBuf(pB, tempCommand, 3);
|
1279 |
|
|
|
1280 |
|
|
#ifdef XXX
|
1281 |
|
|
// enable heartbeat for test porpoises
|
1282 |
|
|
CHANNEL_OF(tempCommand) = 0;
|
1283 |
|
|
PTYPE_OF(tempCommand) = PTYPE_BYPASS;
|
1284 |
|
|
CMD_COUNT_OF(tempCommand) = 2;
|
1285 |
|
|
(CMD_OF(tempCommand))[0] = 44; /* get ping */
|
1286 |
|
|
(CMD_OF(tempCommand))[1] = 200; /* 200 ms */
|
1287 |
|
|
WRITE_LOCK_IRQSAVE(&pB->write_fifo_spinlock,flags);
|
1288 |
|
|
iiWriteBuf(pB, tempCommand, 4);
|
1289 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pB->write_fifo_spinlock,flags);
|
1290 |
|
|
#endif
|
1291 |
|
|
|
1292 |
|
|
iiEnableMailIrq(pB);
|
1293 |
|
|
iiSendPendingMail(pB);
|
1294 |
|
|
}
|
1295 |
|
|
|
1296 |
|
|
/******************************************************************************/
|
1297 |
|
|
/* Interrupt Handler Section */
|
1298 |
|
|
/******************************************************************************/
|
1299 |
|
|
|
1300 |
|
|
static inline void
|
1301 |
|
|
service_all_boards()
|
1302 |
|
|
{
|
1303 |
|
|
int i;
|
1304 |
|
|
i2eBordStrPtr pB;
|
1305 |
|
|
|
1306 |
|
|
/* Service every board on the list */
|
1307 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
1308 |
|
|
pB = i2BoardPtrTable[i];
|
1309 |
|
|
if ( pB ) {
|
1310 |
|
|
i2ServiceBoard( pB );
|
1311 |
|
|
}
|
1312 |
|
|
}
|
1313 |
|
|
}
|
1314 |
|
|
|
1315 |
|
|
|
1316 |
|
|
/******************************************************************************/
|
1317 |
|
|
/* Function: ip2_interrupt_bh(pB) */
|
1318 |
|
|
/* Parameters: pB - pointer to the board structure */
|
1319 |
|
|
/* Returns: Nothing */
|
1320 |
|
|
/* */
|
1321 |
|
|
/* Description: */
|
1322 |
|
|
/* Service the board in a bottom half interrupt handler and then */
|
1323 |
|
|
/* reenable the board's interrupts if it has an IRQ number */
|
1324 |
|
|
/* */
|
1325 |
|
|
/******************************************************************************/
|
1326 |
|
|
static void
|
1327 |
|
|
ip2_interrupt_bh(i2eBordStrPtr pB)
|
1328 |
|
|
{
|
1329 |
|
|
// pB better well be set or we have a problem! We can only get
|
1330 |
|
|
// here from the IMMEDIATE queue. Here, we process the boards.
|
1331 |
|
|
// Checking pB doesn't cost much and it saves us from the sanity checkers.
|
1332 |
|
|
|
1333 |
|
|
bh_counter++;
|
1334 |
|
|
|
1335 |
|
|
if ( pB ) {
|
1336 |
|
|
i2ServiceBoard( pB );
|
1337 |
|
|
if( pB->i2eUsingIrq ) {
|
1338 |
|
|
// Re-enable his interrupts
|
1339 |
|
|
iiEnableMailIrq(pB);
|
1340 |
|
|
}
|
1341 |
|
|
}
|
1342 |
|
|
}
|
1343 |
|
|
|
1344 |
|
|
|
1345 |
|
|
/******************************************************************************/
|
1346 |
|
|
/* Function: ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs) */
|
1347 |
|
|
/* Parameters: irq - interrupt number */
|
1348 |
|
|
/* pointer to optional device ID structure */
|
1349 |
|
|
/* pointer to register structure */
|
1350 |
|
|
/* Returns: Nothing */
|
1351 |
|
|
/* */
|
1352 |
|
|
/* Description: */
|
1353 |
|
|
/* */
|
1354 |
|
|
/* Our task here is simply to identify each board which needs servicing. */
|
1355 |
|
|
/* If we are queuing then, queue it to be serviced, and disable its irq */
|
1356 |
|
|
/* mask otherwise process the board directly. */
|
1357 |
|
|
/* */
|
1358 |
|
|
/* We could queue by IRQ but that just complicates things on both ends */
|
1359 |
|
|
/* with very little gain in performance (how many instructions does */
|
1360 |
|
|
/* it take to iterate on the immediate queue). */
|
1361 |
|
|
/* */
|
1362 |
|
|
/* */
|
1363 |
|
|
/******************************************************************************/
|
1364 |
|
|
static void
|
1365 |
|
|
ip2_interrupt(int irq, void *dev_id, struct pt_regs * regs)
|
1366 |
|
|
{
|
1367 |
|
|
int i;
|
1368 |
|
|
i2eBordStrPtr pB;
|
1369 |
|
|
|
1370 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq );
|
1371 |
|
|
|
1372 |
|
|
/* Service just the boards on the list using this irq */
|
1373 |
|
|
for( i = 0; i < i2nBoards; ++i ) {
|
1374 |
|
|
pB = i2BoardPtrTable[i];
|
1375 |
|
|
|
1376 |
|
|
// Only process those boards which match our IRQ.
|
1377 |
|
|
// IRQ = 0 for polled boards, we won't poll "IRQ" boards
|
1378 |
|
|
|
1379 |
|
|
if ( pB && (pB->i2eUsingIrq == irq) ) {
|
1380 |
|
|
#ifdef USE_IQI
|
1381 |
|
|
|
1382 |
|
|
if (NO_MAIL_HERE != ( pB->i2eStartMail = iiGetMail(pB))) {
|
1383 |
|
|
// Disable his interrupt (will be enabled when serviced)
|
1384 |
|
|
// This is mostly to protect from reentrancy.
|
1385 |
|
|
iiDisableMailIrq(pB);
|
1386 |
|
|
|
1387 |
|
|
// Park the board on the immediate queue for processing.
|
1388 |
|
|
queue_task(&pB->tqueue_interrupt, &tq_immediate);
|
1389 |
|
|
|
1390 |
|
|
// Make sure the immediate queue is flagged to fire.
|
1391 |
|
|
mark_bh(IMMEDIATE_BH);
|
1392 |
|
|
}
|
1393 |
|
|
#else
|
1394 |
|
|
// We are using immediate servicing here. This sucks and can
|
1395 |
|
|
// cause all sorts of havoc with ppp and others. The failsafe
|
1396 |
|
|
// check on iiSendPendingMail could also throw a hairball.
|
1397 |
|
|
i2ServiceBoard( pB );
|
1398 |
|
|
#endif /* USE_IQI */
|
1399 |
|
|
}
|
1400 |
|
|
}
|
1401 |
|
|
|
1402 |
|
|
++irq_counter;
|
1403 |
|
|
|
1404 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
|
1405 |
|
|
}
|
1406 |
|
|
|
1407 |
|
|
/******************************************************************************/
|
1408 |
|
|
/* Function: ip2_poll(unsigned long arg) */
|
1409 |
|
|
/* Parameters: ? */
|
1410 |
|
|
/* Returns: Nothing */
|
1411 |
|
|
/* */
|
1412 |
|
|
/* Description: */
|
1413 |
|
|
/* This function calls the library routine i2ServiceBoard for each board in */
|
1414 |
|
|
/* the board table. This is used instead of the interrupt routine when polled */
|
1415 |
|
|
/* mode is specified. */
|
1416 |
|
|
/******************************************************************************/
|
1417 |
|
|
static void
|
1418 |
|
|
ip2_poll(unsigned long arg)
|
1419 |
|
|
{
|
1420 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 );
|
1421 |
|
|
|
1422 |
|
|
TimerOn = 0; // it's the truth but not checked in service
|
1423 |
|
|
|
1424 |
|
|
// Just polled boards, IRQ = 0 will hit all non-interrupt boards.
|
1425 |
|
|
// It will NOT poll boards handled by hard interrupts.
|
1426 |
|
|
// The issue of queued BH interrups is handled in ip2_interrupt().
|
1427 |
|
|
ip2_interrupt(0, NULL, NULL);
|
1428 |
|
|
|
1429 |
|
|
PollTimer.expires = POLL_TIMEOUT;
|
1430 |
|
|
add_timer( &PollTimer );
|
1431 |
|
|
TimerOn = 1;
|
1432 |
|
|
|
1433 |
|
|
ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 );
|
1434 |
|
|
}
|
1435 |
|
|
|
1436 |
|
|
static inline void
|
1437 |
|
|
do_input( i2ChanStrPtr pCh )
|
1438 |
|
|
{
|
1439 |
|
|
unsigned long flags;
|
1440 |
|
|
|
1441 |
|
|
ip2trace(CHANN, ITRC_INPUT, 21, 0 );
|
1442 |
|
|
|
1443 |
|
|
// Data input
|
1444 |
|
|
if ( pCh->pTTY != NULL ) {
|
1445 |
|
|
READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags)
|
1446 |
|
|
if (!pCh->throttled && (pCh->Ibuf_stuff != pCh->Ibuf_strip)) {
|
1447 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags)
|
1448 |
|
|
i2Input( pCh );
|
1449 |
|
|
} else
|
1450 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags)
|
1451 |
|
|
} else {
|
1452 |
|
|
ip2trace(CHANN, ITRC_INPUT, 22, 0 );
|
1453 |
|
|
|
1454 |
|
|
i2InputFlush( pCh );
|
1455 |
|
|
}
|
1456 |
|
|
}
|
1457 |
|
|
|
1458 |
|
|
// code duplicated from n_tty (ldisc)
|
1459 |
|
|
static inline void
|
1460 |
|
|
isig(int sig, struct tty_struct *tty, int flush)
|
1461 |
|
|
{
|
1462 |
|
|
if (tty->pgrp > 0)
|
1463 |
|
|
kill_pg(tty->pgrp, sig, 1);
|
1464 |
|
|
if (flush || !L_NOFLSH(tty)) {
|
1465 |
|
|
if ( tty->ldisc.flush_buffer )
|
1466 |
|
|
tty->ldisc.flush_buffer(tty);
|
1467 |
|
|
i2InputFlush( tty->driver_data );
|
1468 |
|
|
}
|
1469 |
|
|
}
|
1470 |
|
|
|
1471 |
|
|
static inline void
|
1472 |
|
|
do_status( i2ChanStrPtr pCh )
|
1473 |
|
|
{
|
1474 |
|
|
int status;
|
1475 |
|
|
|
1476 |
|
|
status = i2GetStatus( pCh, (I2_BRK|I2_PAR|I2_FRA|I2_OVR) );
|
1477 |
|
|
|
1478 |
|
|
ip2trace (CHANN, ITRC_STATUS, 21, 1, status );
|
1479 |
|
|
|
1480 |
|
|
if (pCh->pTTY && (status & (I2_BRK|I2_PAR|I2_FRA|I2_OVR)) ) {
|
1481 |
|
|
if ( (status & I2_BRK) ) {
|
1482 |
|
|
// code duplicated from n_tty (ldisc)
|
1483 |
|
|
if (I_IGNBRK(pCh->pTTY))
|
1484 |
|
|
goto skip_this;
|
1485 |
|
|
if (I_BRKINT(pCh->pTTY)) {
|
1486 |
|
|
isig(SIGINT, pCh->pTTY, 1);
|
1487 |
|
|
goto skip_this;
|
1488 |
|
|
}
|
1489 |
|
|
wake_up_interruptible(&pCh->pTTY->read_wait);
|
1490 |
|
|
}
|
1491 |
|
|
#ifdef NEVER_HAPPENS_AS_SETUP_XXX
|
1492 |
|
|
// and can't work because we don't know the_char
|
1493 |
|
|
// as the_char is reported on a seperate path
|
1494 |
|
|
// The intelligent board does this stuff as setup
|
1495 |
|
|
{
|
1496 |
|
|
char brkf = TTY_NORMAL;
|
1497 |
|
|
unsigned char brkc = '\0';
|
1498 |
|
|
unsigned char tmp;
|
1499 |
|
|
if ( (status & I2_BRK) ) {
|
1500 |
|
|
brkf = TTY_BREAK;
|
1501 |
|
|
brkc = '\0';
|
1502 |
|
|
}
|
1503 |
|
|
else if (status & I2_PAR) {
|
1504 |
|
|
brkf = TTY_PARITY;
|
1505 |
|
|
brkc = the_char;
|
1506 |
|
|
} else if (status & I2_FRA) {
|
1507 |
|
|
brkf = TTY_FRAME;
|
1508 |
|
|
brkc = the_char;
|
1509 |
|
|
} else if (status & I2_OVR) {
|
1510 |
|
|
brkf = TTY_OVERRUN;
|
1511 |
|
|
brkc = the_char;
|
1512 |
|
|
}
|
1513 |
|
|
tmp = pCh->pTTY->real_raw;
|
1514 |
|
|
pCh->pTTY->real_raw = 0;
|
1515 |
|
|
pCh->pTTY->ldisc.receive_buf( pCh->pTTY, &brkc, &brkf, 1 );
|
1516 |
|
|
pCh->pTTY->real_raw = tmp;
|
1517 |
|
|
}
|
1518 |
|
|
#endif /* NEVER_HAPPENS_AS_SETUP_XXX */
|
1519 |
|
|
}
|
1520 |
|
|
skip_this:
|
1521 |
|
|
|
1522 |
|
|
if ( status & (I2_DDCD | I2_DDSR | I2_DCTS | I2_DRI) ) {
|
1523 |
|
|
wake_up_interruptible(&pCh->delta_msr_wait);
|
1524 |
|
|
|
1525 |
|
|
if ( (pCh->flags & ASYNC_CHECK_CD) && (status & I2_DDCD) ) {
|
1526 |
|
|
if ( status & I2_DCD ) {
|
1527 |
|
|
if ( pCh->wopen ) {
|
1528 |
|
|
wake_up_interruptible ( &pCh->open_wait );
|
1529 |
|
|
}
|
1530 |
|
|
} else if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) ) {
|
1531 |
|
|
if (pCh->pTTY && (!(pCh->pTTY->termios->c_cflag & CLOCAL)) ) {
|
1532 |
|
|
tty_hangup( pCh->pTTY );
|
1533 |
|
|
}
|
1534 |
|
|
}
|
1535 |
|
|
}
|
1536 |
|
|
}
|
1537 |
|
|
|
1538 |
|
|
ip2trace (CHANN, ITRC_STATUS, 26, 0 );
|
1539 |
|
|
}
|
1540 |
|
|
|
1541 |
|
|
/******************************************************************************/
|
1542 |
|
|
/* Device Open/Close/Ioctl Entry Point Section */
|
1543 |
|
|
/******************************************************************************/
|
1544 |
|
|
|
1545 |
|
|
/******************************************************************************/
|
1546 |
|
|
/* Function: open_sanity_check() */
|
1547 |
|
|
/* Parameters: Pointer to tty structure */
|
1548 |
|
|
/* Pointer to file structure */
|
1549 |
|
|
/* Returns: Success or failure */
|
1550 |
|
|
/* */
|
1551 |
|
|
/* Description: */
|
1552 |
|
|
/* Verifies the structure magic numbers and cross links. */
|
1553 |
|
|
/******************************************************************************/
|
1554 |
|
|
#ifdef IP2DEBUG_OPEN
|
1555 |
|
|
static void
|
1556 |
|
|
open_sanity_check( i2ChanStrPtr pCh, i2eBordStrPtr pBrd )
|
1557 |
|
|
{
|
1558 |
|
|
if ( pBrd->i2eValid != I2E_MAGIC ) {
|
1559 |
|
|
printk(KERN_ERR "IP2: invalid board structure\n" );
|
1560 |
|
|
} else if ( pBrd != pCh->pMyBord ) {
|
1561 |
|
|
printk(KERN_ERR "IP2: board structure pointer mismatch (%p)\n",
|
1562 |
|
|
pCh->pMyBord );
|
1563 |
|
|
} else if ( pBrd->i2eChannelCnt < pCh->port_index ) {
|
1564 |
|
|
printk(KERN_ERR "IP2: bad device index (%d)\n", pCh->port_index );
|
1565 |
|
|
} else if (&((i2ChanStrPtr)pBrd->i2eChannelPtr)[pCh->port_index] != pCh) {
|
1566 |
|
|
} else {
|
1567 |
|
|
printk(KERN_INFO "IP2: all pointers check out!\n" );
|
1568 |
|
|
}
|
1569 |
|
|
}
|
1570 |
|
|
#endif
|
1571 |
|
|
|
1572 |
|
|
|
1573 |
|
|
/******************************************************************************/
|
1574 |
|
|
/* Function: ip2_open() */
|
1575 |
|
|
/* Parameters: Pointer to tty structure */
|
1576 |
|
|
/* Pointer to file structure */
|
1577 |
|
|
/* Returns: Success or failure */
|
1578 |
|
|
/* */
|
1579 |
|
|
/* Description: (MANDATORY) */
|
1580 |
|
|
/* A successful device open has to run a gauntlet of checks before it */
|
1581 |
|
|
/* completes. After some sanity checking and pointer setup, the function */
|
1582 |
|
|
/* blocks until all conditions are satisfied. It then initialises the port to */
|
1583 |
|
|
/* the default characteristics and returns. */
|
1584 |
|
|
/******************************************************************************/
|
1585 |
|
|
static int
|
1586 |
|
|
ip2_open( PTTY tty, struct file *pFile )
|
1587 |
|
|
{
|
1588 |
|
|
wait_queue_t wait;
|
1589 |
|
|
int rc = 0;
|
1590 |
|
|
int do_clocal = 0;
|
1591 |
|
|
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
|
1592 |
|
|
|
1593 |
|
|
ip2trace (MINOR(tty->device), ITRC_OPEN, ITRC_ENTER, 0 );
|
1594 |
|
|
|
1595 |
|
|
if ( pCh == NULL ) {
|
1596 |
|
|
return -ENODEV;
|
1597 |
|
|
}
|
1598 |
|
|
/* Setup pointer links in device and tty structures */
|
1599 |
|
|
pCh->pTTY = tty;
|
1600 |
|
|
tty->driver_data = pCh;
|
1601 |
|
|
MOD_INC_USE_COUNT;
|
1602 |
|
|
|
1603 |
|
|
#ifdef IP2DEBUG_OPEN
|
1604 |
|
|
printk(KERN_DEBUG \
|
1605 |
|
|
"IP2:open(tty=%p,pFile=%p):dev=%x,maj=%d,min=%d,ch=%d,idx=%d\n",
|
1606 |
|
|
tty, pFile, tty->device, MAJOR(tty->device), MINOR(tty->device),
|
1607 |
|
|
pCh->infl.hd.i2sChannel, pCh->port_index);
|
1608 |
|
|
open_sanity_check ( pCh, pCh->pMyBord );
|
1609 |
|
|
#endif
|
1610 |
|
|
|
1611 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 3, CMD_DTRUP,CMD_RTSUP,CMD_DCD_REP);
|
1612 |
|
|
pCh->dataSetOut |= (I2_DTR | I2_RTS);
|
1613 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
1614 |
|
|
|
1615 |
|
|
/* Block here until the port is ready (per serial and istallion) */
|
1616 |
|
|
/*
|
1617 |
|
|
* 1. If the port is in the middle of closing wait for the completion
|
1618 |
|
|
* and then return the appropriate error.
|
1619 |
|
|
*/
|
1620 |
|
|
init_waitqueue_entry(&wait, current);
|
1621 |
|
|
add_wait_queue(&pCh->close_wait, &wait);
|
1622 |
|
|
set_current_state( TASK_INTERRUPTIBLE );
|
1623 |
|
|
|
1624 |
|
|
if ( tty_hung_up_p(pFile) || ( pCh->flags & ASYNC_CLOSING )) {
|
1625 |
|
|
if ( pCh->flags & ASYNC_CLOSING ) {
|
1626 |
|
|
schedule();
|
1627 |
|
|
}
|
1628 |
|
|
if ( tty_hung_up_p(pFile) ) {
|
1629 |
|
|
set_current_state( TASK_RUNNING );
|
1630 |
|
|
remove_wait_queue(&pCh->close_wait, &wait);
|
1631 |
|
|
return( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS;
|
1632 |
|
|
}
|
1633 |
|
|
}
|
1634 |
|
|
set_current_state( TASK_RUNNING );
|
1635 |
|
|
remove_wait_queue(&pCh->close_wait, &wait);
|
1636 |
|
|
|
1637 |
|
|
/*
|
1638 |
|
|
* 2. If this is a callout device, make sure the normal port is not in
|
1639 |
|
|
* use, and that someone else doesn't have the callout device locked.
|
1640 |
|
|
* (These are the only tests the standard serial driver makes for
|
1641 |
|
|
* callout devices.)
|
1642 |
|
|
*/
|
1643 |
|
|
if ( tty->driver.subtype == SERIAL_TYPE_CALLOUT ) {
|
1644 |
|
|
if ( pCh->flags & ASYNC_NORMAL_ACTIVE ) {
|
1645 |
|
|
return -EBUSY;
|
1646 |
|
|
}
|
1647 |
|
|
if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) &&
|
1648 |
|
|
( pCh->flags & ASYNC_SESSION_LOCKOUT ) &&
|
1649 |
|
|
( pCh->session != current->session ) ) {
|
1650 |
|
|
return -EBUSY;
|
1651 |
|
|
}
|
1652 |
|
|
if ( ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) &&
|
1653 |
|
|
( pCh->flags & ASYNC_PGRP_LOCKOUT ) &&
|
1654 |
|
|
( pCh->pgrp != current->pgrp ) ) {
|
1655 |
|
|
return -EBUSY;
|
1656 |
|
|
}
|
1657 |
|
|
pCh->flags |= ASYNC_CALLOUT_ACTIVE;
|
1658 |
|
|
goto noblock;
|
1659 |
|
|
}
|
1660 |
|
|
/*
|
1661 |
|
|
* 3. Handle a non-blocking open of a normal port.
|
1662 |
|
|
*/
|
1663 |
|
|
if ( (pFile->f_flags & O_NONBLOCK) || (tty->flags & (1<<TTY_IO_ERROR) )) {
|
1664 |
|
|
if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) {
|
1665 |
|
|
return -EBUSY;
|
1666 |
|
|
}
|
1667 |
|
|
pCh->flags |= ASYNC_NORMAL_ACTIVE;
|
1668 |
|
|
goto noblock;
|
1669 |
|
|
}
|
1670 |
|
|
/*
|
1671 |
|
|
* 4. Now loop waiting for the port to be free and carrier present
|
1672 |
|
|
* (if required).
|
1673 |
|
|
*/
|
1674 |
|
|
if ( pCh->flags & ASYNC_CALLOUT_ACTIVE ) {
|
1675 |
|
|
if ( pCh->NormalTermios.c_cflag & CLOCAL ) {
|
1676 |
|
|
do_clocal = 1;
|
1677 |
|
|
}
|
1678 |
|
|
} else {
|
1679 |
|
|
if ( tty->termios->c_cflag & CLOCAL ) {
|
1680 |
|
|
do_clocal = 1;
|
1681 |
|
|
}
|
1682 |
|
|
}
|
1683 |
|
|
|
1684 |
|
|
#ifdef IP2DEBUG_OPEN
|
1685 |
|
|
printk(KERN_DEBUG "OpenBlock: do_clocal = %d\n", do_clocal);
|
1686 |
|
|
#endif
|
1687 |
|
|
|
1688 |
|
|
++pCh->wopen;
|
1689 |
|
|
|
1690 |
|
|
init_waitqueue_entry(&wait, current);
|
1691 |
|
|
add_wait_queue(&pCh->open_wait, &wait);
|
1692 |
|
|
|
1693 |
|
|
for(;;) {
|
1694 |
|
|
if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE)) {
|
1695 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
|
1696 |
|
|
pCh->dataSetOut |= (I2_DTR | I2_RTS);
|
1697 |
|
|
set_current_state( TASK_INTERRUPTIBLE );
|
1698 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
1699 |
|
|
}
|
1700 |
|
|
if ( tty_hung_up_p(pFile) ) {
|
1701 |
|
|
set_current_state( TASK_RUNNING );
|
1702 |
|
|
remove_wait_queue(&pCh->open_wait, &wait);
|
1703 |
|
|
return ( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EBUSY : -ERESTARTSYS;
|
1704 |
|
|
}
|
1705 |
|
|
if ( !(pCh->flags & ASYNC_CALLOUT_ACTIVE) &&
|
1706 |
|
|
!(pCh->flags & ASYNC_CLOSING) &&
|
1707 |
|
|
(do_clocal || (pCh->dataSetIn & I2_DCD) )) {
|
1708 |
|
|
rc = 0;
|
1709 |
|
|
break;
|
1710 |
|
|
}
|
1711 |
|
|
|
1712 |
|
|
#ifdef IP2DEBUG_OPEN
|
1713 |
|
|
printk(KERN_DEBUG "ASYNC_CALLOUT_ACTIVE = %s\n",
|
1714 |
|
|
(pCh->flags & ASYNC_CALLOUT_ACTIVE)?"True":"False");
|
1715 |
|
|
printk(KERN_DEBUG "ASYNC_CLOSING = %s\n",
|
1716 |
|
|
(pCh->flags & ASYNC_CLOSING)?"True":"False");
|
1717 |
|
|
printk(KERN_DEBUG "OpenBlock: waiting for CD or signal\n");
|
1718 |
|
|
#endif
|
1719 |
|
|
ip2trace (CHANN, ITRC_OPEN, 3, 2, (pCh->flags & ASYNC_CALLOUT_ACTIVE),
|
1720 |
|
|
(pCh->flags & ASYNC_CLOSING) );
|
1721 |
|
|
/* check for signal */
|
1722 |
|
|
if (signal_pending(current)) {
|
1723 |
|
|
rc = (( pCh->flags & ASYNC_HUP_NOTIFY ) ? -EAGAIN : -ERESTARTSYS);
|
1724 |
|
|
break;
|
1725 |
|
|
}
|
1726 |
|
|
schedule();
|
1727 |
|
|
}
|
1728 |
|
|
set_current_state( TASK_RUNNING );
|
1729 |
|
|
remove_wait_queue(&pCh->open_wait, &wait);
|
1730 |
|
|
|
1731 |
|
|
--pCh->wopen; //why count?
|
1732 |
|
|
|
1733 |
|
|
ip2trace (CHANN, ITRC_OPEN, 4, 0 );
|
1734 |
|
|
|
1735 |
|
|
if (rc != 0 ) {
|
1736 |
|
|
return rc;
|
1737 |
|
|
}
|
1738 |
|
|
pCh->flags |= ASYNC_NORMAL_ACTIVE;
|
1739 |
|
|
|
1740 |
|
|
noblock:
|
1741 |
|
|
|
1742 |
|
|
/* first open - Assign termios structure to port */
|
1743 |
|
|
if ( tty->count == 1 ) {
|
1744 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
|
1745 |
|
|
if ( pCh->flags & ASYNC_SPLIT_TERMIOS ) {
|
1746 |
|
|
if ( tty->driver.subtype == SERIAL_TYPE_NORMAL ) {
|
1747 |
|
|
*tty->termios = pCh->NormalTermios;
|
1748 |
|
|
} else {
|
1749 |
|
|
*tty->termios = pCh->CalloutTermios;
|
1750 |
|
|
}
|
1751 |
|
|
}
|
1752 |
|
|
/* Now we must send the termios settings to the loadware */
|
1753 |
|
|
set_params( pCh, NULL );
|
1754 |
|
|
}
|
1755 |
|
|
|
1756 |
|
|
/* override previous and never reset ??? */
|
1757 |
|
|
pCh->session = current->session;
|
1758 |
|
|
pCh->pgrp = current->pgrp;
|
1759 |
|
|
|
1760 |
|
|
/*
|
1761 |
|
|
* Now set any i2lib options. These may go away if the i2lib code ends
|
1762 |
|
|
* up rolled into the mainline.
|
1763 |
|
|
*/
|
1764 |
|
|
pCh->channelOptions |= CO_NBLOCK_WRITE;
|
1765 |
|
|
|
1766 |
|
|
#ifdef IP2DEBUG_OPEN
|
1767 |
|
|
printk (KERN_DEBUG "IP2: open completed\n" );
|
1768 |
|
|
#endif
|
1769 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
1770 |
|
|
|
1771 |
|
|
ip2trace (CHANN, ITRC_OPEN, ITRC_RETURN, 0 );
|
1772 |
|
|
|
1773 |
|
|
return 0;
|
1774 |
|
|
}
|
1775 |
|
|
|
1776 |
|
|
/******************************************************************************/
|
1777 |
|
|
/* Function: ip2_close() */
|
1778 |
|
|
/* Parameters: Pointer to tty structure */
|
1779 |
|
|
/* Pointer to file structure */
|
1780 |
|
|
/* Returns: Nothing */
|
1781 |
|
|
/* */
|
1782 |
|
|
/* Description: */
|
1783 |
|
|
/* */
|
1784 |
|
|
/* */
|
1785 |
|
|
/******************************************************************************/
|
1786 |
|
|
static void
|
1787 |
|
|
ip2_close( PTTY tty, struct file *pFile )
|
1788 |
|
|
{
|
1789 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
1790 |
|
|
|
1791 |
|
|
if ( !pCh ) {
|
1792 |
|
|
return;
|
1793 |
|
|
}
|
1794 |
|
|
|
1795 |
|
|
ip2trace (CHANN, ITRC_CLOSE, ITRC_ENTER, 0 );
|
1796 |
|
|
|
1797 |
|
|
#ifdef IP2DEBUG_OPEN
|
1798 |
|
|
printk(KERN_DEBUG "IP2:close ttyF%02X:\n",MINOR(tty->device));
|
1799 |
|
|
#endif
|
1800 |
|
|
|
1801 |
|
|
if ( tty_hung_up_p ( pFile ) ) {
|
1802 |
|
|
MOD_DEC_USE_COUNT;
|
1803 |
|
|
|
1804 |
|
|
ip2trace (CHANN, ITRC_CLOSE, 2, 1, 2 );
|
1805 |
|
|
|
1806 |
|
|
return;
|
1807 |
|
|
}
|
1808 |
|
|
if ( tty->count > 1 ) { /* not the last close */
|
1809 |
|
|
MOD_DEC_USE_COUNT;
|
1810 |
|
|
|
1811 |
|
|
ip2trace (CHANN, ITRC_CLOSE, 2, 1, 3 );
|
1812 |
|
|
|
1813 |
|
|
return;
|
1814 |
|
|
}
|
1815 |
|
|
pCh->flags |= ASYNC_CLOSING; // last close actually
|
1816 |
|
|
|
1817 |
|
|
/*
|
1818 |
|
|
* Save the termios structure, since this port may have separate termios
|
1819 |
|
|
* for callout and dialin.
|
1820 |
|
|
*/
|
1821 |
|
|
if (pCh->flags & ASYNC_NORMAL_ACTIVE)
|
1822 |
|
|
pCh->NormalTermios = *tty->termios;
|
1823 |
|
|
if (pCh->flags & ASYNC_CALLOUT_ACTIVE)
|
1824 |
|
|
pCh->CalloutTermios = *tty->termios;
|
1825 |
|
|
|
1826 |
|
|
tty->closing = 1;
|
1827 |
|
|
|
1828 |
|
|
if (pCh->ClosingWaitTime != ASYNC_CLOSING_WAIT_NONE) {
|
1829 |
|
|
/*
|
1830 |
|
|
* Before we drop DTR, make sure the transmitter has completely drained.
|
1831 |
|
|
* This uses an timeout, after which the close
|
1832 |
|
|
* completes.
|
1833 |
|
|
*/
|
1834 |
|
|
ip2_wait_until_sent(tty, pCh->ClosingWaitTime );
|
1835 |
|
|
}
|
1836 |
|
|
/*
|
1837 |
|
|
* At this point we stop accepting input. Here we flush the channel
|
1838 |
|
|
* input buffer which will allow the board to send up more data. Any
|
1839 |
|
|
* additional input is tossed at interrupt/poll time.
|
1840 |
|
|
*/
|
1841 |
|
|
i2InputFlush( pCh );
|
1842 |
|
|
|
1843 |
|
|
/* disable DSS reporting */
|
1844 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 4,
|
1845 |
|
|
CMD_DCD_NREP, CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
|
1846 |
|
|
if ( !tty || (tty->termios->c_cflag & HUPCL) ) {
|
1847 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
|
1848 |
|
|
pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
|
1849 |
|
|
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
|
1850 |
|
|
}
|
1851 |
|
|
|
1852 |
|
|
serviceOutgoingFifo ( pCh->pMyBord );
|
1853 |
|
|
|
1854 |
|
|
if ( tty->driver.flush_buffer )
|
1855 |
|
|
tty->driver.flush_buffer(tty);
|
1856 |
|
|
if ( tty->ldisc.flush_buffer )
|
1857 |
|
|
tty->ldisc.flush_buffer(tty);
|
1858 |
|
|
tty->closing = 0;
|
1859 |
|
|
|
1860 |
|
|
pCh->pTTY = NULL;
|
1861 |
|
|
|
1862 |
|
|
if (pCh->wopen) {
|
1863 |
|
|
if (pCh->ClosingDelay) {
|
1864 |
|
|
current->state = TASK_INTERRUPTIBLE;
|
1865 |
|
|
schedule_timeout(pCh->ClosingDelay);
|
1866 |
|
|
}
|
1867 |
|
|
wake_up_interruptible(&pCh->open_wait);
|
1868 |
|
|
}
|
1869 |
|
|
|
1870 |
|
|
pCh->flags &=~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
|
1871 |
|
|
wake_up_interruptible(&pCh->close_wait);
|
1872 |
|
|
|
1873 |
|
|
#ifdef IP2DEBUG_OPEN
|
1874 |
|
|
DBG_CNT("ip2_close: after wakeups--");
|
1875 |
|
|
#endif
|
1876 |
|
|
|
1877 |
|
|
MOD_DEC_USE_COUNT;
|
1878 |
|
|
|
1879 |
|
|
ip2trace (CHANN, ITRC_CLOSE, ITRC_RETURN, 1, 1 );
|
1880 |
|
|
|
1881 |
|
|
return;
|
1882 |
|
|
}
|
1883 |
|
|
|
1884 |
|
|
/******************************************************************************/
|
1885 |
|
|
/* Function: ip2_hangup() */
|
1886 |
|
|
/* Parameters: Pointer to tty structure */
|
1887 |
|
|
/* Returns: Nothing */
|
1888 |
|
|
/* */
|
1889 |
|
|
/* Description: */
|
1890 |
|
|
/* */
|
1891 |
|
|
/* */
|
1892 |
|
|
/******************************************************************************/
|
1893 |
|
|
static void
|
1894 |
|
|
ip2_hangup ( PTTY tty )
|
1895 |
|
|
{
|
1896 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
1897 |
|
|
|
1898 |
|
|
if( !pCh ) {
|
1899 |
|
|
return;
|
1900 |
|
|
}
|
1901 |
|
|
|
1902 |
|
|
ip2trace (CHANN, ITRC_HANGUP, ITRC_ENTER, 0 );
|
1903 |
|
|
|
1904 |
|
|
ip2_flush_buffer(tty);
|
1905 |
|
|
|
1906 |
|
|
/* disable DSS reporting */
|
1907 |
|
|
|
1908 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_DCD_NREP);
|
1909 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 0, 2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
|
1910 |
|
|
if ( (tty->termios->c_cflag & HUPCL) ) {
|
1911 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 2, CMD_RTSDN, CMD_DTRDN);
|
1912 |
|
|
pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
|
1913 |
|
|
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
|
1914 |
|
|
}
|
1915 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 1, 3,
|
1916 |
|
|
CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
|
1917 |
|
|
serviceOutgoingFifo ( pCh->pMyBord );
|
1918 |
|
|
|
1919 |
|
|
wake_up_interruptible ( &pCh->delta_msr_wait );
|
1920 |
|
|
|
1921 |
|
|
pCh->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
|
1922 |
|
|
pCh->pTTY = NULL;
|
1923 |
|
|
wake_up_interruptible ( &pCh->open_wait );
|
1924 |
|
|
|
1925 |
|
|
ip2trace (CHANN, ITRC_HANGUP, ITRC_RETURN, 0 );
|
1926 |
|
|
}
|
1927 |
|
|
|
1928 |
|
|
/******************************************************************************/
|
1929 |
|
|
/******************************************************************************/
|
1930 |
|
|
/* Device Output Section */
|
1931 |
|
|
/******************************************************************************/
|
1932 |
|
|
/******************************************************************************/
|
1933 |
|
|
|
1934 |
|
|
/******************************************************************************/
|
1935 |
|
|
/* Function: ip2_write() */
|
1936 |
|
|
/* Parameters: Pointer to tty structure */
|
1937 |
|
|
/* Flag denoting data is in user (1) or kernel (0) space */
|
1938 |
|
|
/* Pointer to data */
|
1939 |
|
|
/* Number of bytes to write */
|
1940 |
|
|
/* Returns: Number of bytes actually written */
|
1941 |
|
|
/* */
|
1942 |
|
|
/* Description: (MANDATORY) */
|
1943 |
|
|
/* */
|
1944 |
|
|
/* */
|
1945 |
|
|
/******************************************************************************/
|
1946 |
|
|
static int
|
1947 |
|
|
ip2_write( PTTY tty, int user, const unsigned char *pData, int count)
|
1948 |
|
|
{
|
1949 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
1950 |
|
|
int bytesSent = 0;
|
1951 |
|
|
unsigned long flags;
|
1952 |
|
|
|
1953 |
|
|
ip2trace (CHANN, ITRC_WRITE, ITRC_ENTER, 2, count, -1 );
|
1954 |
|
|
|
1955 |
|
|
/* Flush out any buffered data left over from ip2_putchar() calls. */
|
1956 |
|
|
ip2_flush_chars( tty );
|
1957 |
|
|
|
1958 |
|
|
/* This is the actual move bit. Make sure it does what we need!!!!! */
|
1959 |
|
|
WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
1960 |
|
|
bytesSent = i2Output( pCh, pData, count, user );
|
1961 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
1962 |
|
|
|
1963 |
|
|
ip2trace (CHANN, ITRC_WRITE, ITRC_RETURN, 1, bytesSent );
|
1964 |
|
|
|
1965 |
|
|
return bytesSent > 0 ? bytesSent : 0;
|
1966 |
|
|
}
|
1967 |
|
|
|
1968 |
|
|
/******************************************************************************/
|
1969 |
|
|
/* Function: ip2_putchar() */
|
1970 |
|
|
/* Parameters: Pointer to tty structure */
|
1971 |
|
|
/* Character to write */
|
1972 |
|
|
/* Returns: Nothing */
|
1973 |
|
|
/* */
|
1974 |
|
|
/* Description: */
|
1975 |
|
|
/* */
|
1976 |
|
|
/* */
|
1977 |
|
|
/******************************************************************************/
|
1978 |
|
|
static void
|
1979 |
|
|
ip2_putchar( PTTY tty, unsigned char ch )
|
1980 |
|
|
{
|
1981 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
1982 |
|
|
unsigned long flags;
|
1983 |
|
|
|
1984 |
|
|
// ip2trace (CHANN, ITRC_PUTC, ITRC_ENTER, 1, ch );
|
1985 |
|
|
|
1986 |
|
|
WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
1987 |
|
|
pCh->Pbuf[pCh->Pbuf_stuff++] = ch;
|
1988 |
|
|
if ( pCh->Pbuf_stuff == sizeof pCh->Pbuf ) {
|
1989 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
1990 |
|
|
ip2_flush_chars( tty );
|
1991 |
|
|
} else
|
1992 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
1993 |
|
|
|
1994 |
|
|
// ip2trace (CHANN, ITRC_PUTC, ITRC_RETURN, 1, ch );
|
1995 |
|
|
}
|
1996 |
|
|
|
1997 |
|
|
/******************************************************************************/
|
1998 |
|
|
/* Function: ip2_flush_chars() */
|
1999 |
|
|
/* Parameters: Pointer to tty structure */
|
2000 |
|
|
/* Returns: Nothing */
|
2001 |
|
|
/* */
|
2002 |
|
|
/* Description: */
|
2003 |
|
|
/* */
|
2004 |
|
|
/******************************************************************************/
|
2005 |
|
|
static void
|
2006 |
|
|
ip2_flush_chars( PTTY tty )
|
2007 |
|
|
{
|
2008 |
|
|
int strip;
|
2009 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2010 |
|
|
unsigned long flags;
|
2011 |
|
|
|
2012 |
|
|
WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
2013 |
|
|
if ( pCh->Pbuf_stuff ) {
|
2014 |
|
|
|
2015 |
|
|
// ip2trace (CHANN, ITRC_PUTC, 10, 1, strip );
|
2016 |
|
|
|
2017 |
|
|
//
|
2018 |
|
|
// We may need to restart i2Output if it does not fullfill this request
|
2019 |
|
|
//
|
2020 |
|
|
strip = i2Output( pCh, pCh->Pbuf, pCh->Pbuf_stuff, 0 );
|
2021 |
|
|
if ( strip != pCh->Pbuf_stuff ) {
|
2022 |
|
|
memmove( pCh->Pbuf, &pCh->Pbuf[strip], pCh->Pbuf_stuff - strip );
|
2023 |
|
|
}
|
2024 |
|
|
pCh->Pbuf_stuff -= strip;
|
2025 |
|
|
}
|
2026 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
2027 |
|
|
}
|
2028 |
|
|
|
2029 |
|
|
/******************************************************************************/
|
2030 |
|
|
/* Function: ip2_write_room() */
|
2031 |
|
|
/* Parameters: Pointer to tty structure */
|
2032 |
|
|
/* Returns: Number of bytes that the driver can accept */
|
2033 |
|
|
/* */
|
2034 |
|
|
/* Description: */
|
2035 |
|
|
/* */
|
2036 |
|
|
/******************************************************************************/
|
2037 |
|
|
static int
|
2038 |
|
|
ip2_write_room ( PTTY tty )
|
2039 |
|
|
{
|
2040 |
|
|
int bytesFree;
|
2041 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2042 |
|
|
unsigned long flags;
|
2043 |
|
|
|
2044 |
|
|
READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
2045 |
|
|
bytesFree = i2OutputFree( pCh ) - pCh->Pbuf_stuff;
|
2046 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
2047 |
|
|
|
2048 |
|
|
ip2trace (CHANN, ITRC_WRITE, 11, 1, bytesFree );
|
2049 |
|
|
|
2050 |
|
|
return ((bytesFree > 0) ? bytesFree : 0);
|
2051 |
|
|
}
|
2052 |
|
|
|
2053 |
|
|
/******************************************************************************/
|
2054 |
|
|
/* Function: ip2_chars_in_buf() */
|
2055 |
|
|
/* Parameters: Pointer to tty structure */
|
2056 |
|
|
/* Returns: Number of bytes queued for transmission */
|
2057 |
|
|
/* */
|
2058 |
|
|
/* Description: */
|
2059 |
|
|
/* */
|
2060 |
|
|
/* */
|
2061 |
|
|
/******************************************************************************/
|
2062 |
|
|
static int
|
2063 |
|
|
ip2_chars_in_buf ( PTTY tty )
|
2064 |
|
|
{
|
2065 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2066 |
|
|
int rc;
|
2067 |
|
|
unsigned long flags;
|
2068 |
|
|
|
2069 |
|
|
ip2trace (CHANN, ITRC_WRITE, 12, 1, pCh->Obuf_char_count + pCh->Pbuf_stuff );
|
2070 |
|
|
|
2071 |
|
|
#ifdef IP2DEBUG_WRITE
|
2072 |
|
|
printk (KERN_DEBUG "IP2: chars in buffer = %d (%d,%d)\n",
|
2073 |
|
|
pCh->Obuf_char_count + pCh->Pbuf_stuff,
|
2074 |
|
|
pCh->Obuf_char_count, pCh->Pbuf_stuff );
|
2075 |
|
|
#endif
|
2076 |
|
|
READ_LOCK_IRQSAVE(&pCh->Obuf_spinlock,flags);
|
2077 |
|
|
rc = pCh->Obuf_char_count;
|
2078 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Obuf_spinlock,flags);
|
2079 |
|
|
READ_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
2080 |
|
|
rc += pCh->Pbuf_stuff;
|
2081 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
2082 |
|
|
return rc;
|
2083 |
|
|
}
|
2084 |
|
|
|
2085 |
|
|
/******************************************************************************/
|
2086 |
|
|
/* Function: ip2_flush_buffer() */
|
2087 |
|
|
/* Parameters: Pointer to tty structure */
|
2088 |
|
|
/* Returns: Nothing */
|
2089 |
|
|
/* */
|
2090 |
|
|
/* Description: */
|
2091 |
|
|
/* */
|
2092 |
|
|
/* */
|
2093 |
|
|
/******************************************************************************/
|
2094 |
|
|
static void
|
2095 |
|
|
ip2_flush_buffer( PTTY tty )
|
2096 |
|
|
{
|
2097 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2098 |
|
|
unsigned long flags;
|
2099 |
|
|
|
2100 |
|
|
ip2trace (CHANN, ITRC_FLUSH, ITRC_ENTER, 0 );
|
2101 |
|
|
|
2102 |
|
|
#ifdef IP2DEBUG_WRITE
|
2103 |
|
|
printk (KERN_DEBUG "IP2: flush buffer\n" );
|
2104 |
|
|
#endif
|
2105 |
|
|
WRITE_LOCK_IRQSAVE(&pCh->Pbuf_spinlock,flags);
|
2106 |
|
|
pCh->Pbuf_stuff = 0;
|
2107 |
|
|
WRITE_UNLOCK_IRQRESTORE(&pCh->Pbuf_spinlock,flags);
|
2108 |
|
|
i2FlushOutput( pCh );
|
2109 |
|
|
ip2_owake(tty);
|
2110 |
|
|
|
2111 |
|
|
ip2trace (CHANN, ITRC_FLUSH, ITRC_RETURN, 0 );
|
2112 |
|
|
|
2113 |
|
|
}
|
2114 |
|
|
|
2115 |
|
|
/******************************************************************************/
|
2116 |
|
|
/* Function: ip2_wait_until_sent() */
|
2117 |
|
|
/* Parameters: Pointer to tty structure */
|
2118 |
|
|
/* Timeout for wait. */
|
2119 |
|
|
/* Returns: Nothing */
|
2120 |
|
|
/* */
|
2121 |
|
|
/* Description: */
|
2122 |
|
|
/* This function is used in place of the normal tty_wait_until_sent, which */
|
2123 |
|
|
/* only waits for the driver buffers to be empty (or rather, those buffers */
|
2124 |
|
|
/* reported by chars_in_buffer) which doesn't work for IP2 due to the */
|
2125 |
|
|
/* indeterminate number of bytes buffered on the board. */
|
2126 |
|
|
/******************************************************************************/
|
2127 |
|
|
static void
|
2128 |
|
|
ip2_wait_until_sent ( PTTY tty, int timeout )
|
2129 |
|
|
{
|
2130 |
|
|
int i = jiffies;
|
2131 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2132 |
|
|
|
2133 |
|
|
tty_wait_until_sent(tty, timeout );
|
2134 |
|
|
if ( (i = timeout - (jiffies -i)) > 0)
|
2135 |
|
|
i2DrainOutput( pCh, i );
|
2136 |
|
|
}
|
2137 |
|
|
|
2138 |
|
|
/******************************************************************************/
|
2139 |
|
|
/******************************************************************************/
|
2140 |
|
|
/* Device Input Section */
|
2141 |
|
|
/******************************************************************************/
|
2142 |
|
|
/******************************************************************************/
|
2143 |
|
|
|
2144 |
|
|
/******************************************************************************/
|
2145 |
|
|
/* Function: ip2_throttle() */
|
2146 |
|
|
/* Parameters: Pointer to tty structure */
|
2147 |
|
|
/* Returns: Nothing */
|
2148 |
|
|
/* */
|
2149 |
|
|
/* Description: */
|
2150 |
|
|
/* */
|
2151 |
|
|
/* */
|
2152 |
|
|
/******************************************************************************/
|
2153 |
|
|
static void
|
2154 |
|
|
ip2_throttle ( PTTY tty )
|
2155 |
|
|
{
|
2156 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2157 |
|
|
|
2158 |
|
|
#ifdef IP2DEBUG_READ
|
2159 |
|
|
printk (KERN_DEBUG "IP2: throttle\n" );
|
2160 |
|
|
#endif
|
2161 |
|
|
/*
|
2162 |
|
|
* Signal the poll/interrupt handlers not to forward incoming data to
|
2163 |
|
|
* the line discipline. This will cause the buffers to fill up in the
|
2164 |
|
|
* library and thus cause the library routines to send the flow control
|
2165 |
|
|
* stuff.
|
2166 |
|
|
*/
|
2167 |
|
|
pCh->throttled = 1;
|
2168 |
|
|
}
|
2169 |
|
|
|
2170 |
|
|
/******************************************************************************/
|
2171 |
|
|
/* Function: ip2_unthrottle() */
|
2172 |
|
|
/* Parameters: Pointer to tty structure */
|
2173 |
|
|
/* Returns: Nothing */
|
2174 |
|
|
/* */
|
2175 |
|
|
/* Description: */
|
2176 |
|
|
/* */
|
2177 |
|
|
/* */
|
2178 |
|
|
/******************************************************************************/
|
2179 |
|
|
static void
|
2180 |
|
|
ip2_unthrottle ( PTTY tty )
|
2181 |
|
|
{
|
2182 |
|
|
i2ChanStrPtr pCh = tty->driver_data;
|
2183 |
|
|
unsigned long flags;
|
2184 |
|
|
|
2185 |
|
|
#ifdef IP2DEBUG_READ
|
2186 |
|
|
printk (KERN_DEBUG "IP2: unthrottle\n" );
|
2187 |
|
|
#endif
|
2188 |
|
|
|
2189 |
|
|
/* Pass incoming data up to the line discipline again. */
|
2190 |
|
|
pCh->throttled = 0;
|
2191 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
|
2192 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2193 |
|
|
READ_LOCK_IRQSAVE(&pCh->Ibuf_spinlock,flags)
|
2194 |
|
|
if ( pCh->Ibuf_stuff != pCh->Ibuf_strip ) {
|
2195 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags)
|
2196 |
|
|
#ifdef IP2DEBUG_READ
|
2197 |
|
|
printk (KERN_DEBUG "i2Input called from unthrottle\n" );
|
2198 |
|
|
#endif
|
2199 |
|
|
i2Input( pCh );
|
2200 |
|
|
} else
|
2201 |
|
|
READ_UNLOCK_IRQRESTORE(&pCh->Ibuf_spinlock,flags)
|
2202 |
|
|
}
|
2203 |
|
|
|
2204 |
|
|
static void
|
2205 |
|
|
ip2_start ( PTTY tty )
|
2206 |
|
|
{
|
2207 |
|
|
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
|
2208 |
|
|
|
2209 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 0, 1, CMD_RESUME);
|
2210 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_UNSUSPEND);
|
2211 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_RESUME);
|
2212 |
|
|
#ifdef IP2DEBUG_WRITE
|
2213 |
|
|
printk (KERN_DEBUG "IP2: start tx\n" );
|
2214 |
|
|
#endif
|
2215 |
|
|
}
|
2216 |
|
|
|
2217 |
|
|
static void
|
2218 |
|
|
ip2_stop ( PTTY tty )
|
2219 |
|
|
{
|
2220 |
|
|
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
|
2221 |
|
|
|
2222 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_SUSPEND);
|
2223 |
|
|
#ifdef IP2DEBUG_WRITE
|
2224 |
|
|
printk (KERN_DEBUG "IP2: stop tx\n" );
|
2225 |
|
|
#endif
|
2226 |
|
|
}
|
2227 |
|
|
|
2228 |
|
|
/******************************************************************************/
|
2229 |
|
|
/* Device Ioctl Section */
|
2230 |
|
|
/******************************************************************************/
|
2231 |
|
|
|
2232 |
|
|
/******************************************************************************/
|
2233 |
|
|
/* Function: ip2_ioctl() */
|
2234 |
|
|
/* Parameters: Pointer to tty structure */
|
2235 |
|
|
/* Pointer to file structure */
|
2236 |
|
|
/* Command */
|
2237 |
|
|
/* Argument */
|
2238 |
|
|
/* Returns: Success or failure */
|
2239 |
|
|
/* */
|
2240 |
|
|
/* Description: */
|
2241 |
|
|
/* */
|
2242 |
|
|
/* */
|
2243 |
|
|
/******************************************************************************/
|
2244 |
|
|
static int
|
2245 |
|
|
ip2_ioctl ( PTTY tty, struct file *pFile, UINT cmd, ULONG arg )
|
2246 |
|
|
{
|
2247 |
|
|
wait_queue_t wait;
|
2248 |
|
|
i2ChanStrPtr pCh = DevTable[MINOR(tty->device)];
|
2249 |
|
|
struct async_icount cprev, cnow; /* kernel counter temps */
|
2250 |
|
|
struct serial_icounter_struct *p_cuser; /* user space */
|
2251 |
|
|
int rc = 0;
|
2252 |
|
|
unsigned long flags;
|
2253 |
|
|
|
2254 |
|
|
if ( pCh == NULL ) {
|
2255 |
|
|
return -ENODEV;
|
2256 |
|
|
}
|
2257 |
|
|
|
2258 |
|
|
ip2trace (CHANN, ITRC_IOCTL, ITRC_ENTER, 2, cmd, arg );
|
2259 |
|
|
|
2260 |
|
|
#ifdef IP2DEBUG_IOCTL
|
2261 |
|
|
printk(KERN_DEBUG "IP2: ioctl cmd (%x), arg (%lx)\n", cmd, arg );
|
2262 |
|
|
#endif
|
2263 |
|
|
|
2264 |
|
|
switch(cmd) {
|
2265 |
|
|
case TIOCGSERIAL:
|
2266 |
|
|
|
2267 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 2, 1, rc );
|
2268 |
|
|
|
2269 |
|
|
rc = get_serial_info(pCh, (struct serial_struct *) arg);
|
2270 |
|
|
if (rc)
|
2271 |
|
|
return rc;
|
2272 |
|
|
break;
|
2273 |
|
|
|
2274 |
|
|
case TIOCSSERIAL:
|
2275 |
|
|
|
2276 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 3, 1, rc );
|
2277 |
|
|
|
2278 |
|
|
rc = set_serial_info(pCh, (struct serial_struct *) arg);
|
2279 |
|
|
if (rc)
|
2280 |
|
|
return rc;
|
2281 |
|
|
break;
|
2282 |
|
|
|
2283 |
|
|
case TCXONC:
|
2284 |
|
|
rc = tty_check_change(tty);
|
2285 |
|
|
if (rc)
|
2286 |
|
|
return rc;
|
2287 |
|
|
switch (arg) {
|
2288 |
|
|
case TCOOFF:
|
2289 |
|
|
//return -ENOIOCTLCMD;
|
2290 |
|
|
break;
|
2291 |
|
|
case TCOON:
|
2292 |
|
|
//return -ENOIOCTLCMD;
|
2293 |
|
|
break;
|
2294 |
|
|
case TCIOFF:
|
2295 |
|
|
if (STOP_CHAR(tty) != __DISABLED_CHAR) {
|
2296 |
|
|
i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
|
2297 |
|
|
CMD_XMIT_NOW(STOP_CHAR(tty)));
|
2298 |
|
|
}
|
2299 |
|
|
break;
|
2300 |
|
|
case TCION:
|
2301 |
|
|
if (START_CHAR(tty) != __DISABLED_CHAR) {
|
2302 |
|
|
i2QueueCommands( PTYPE_BYPASS, pCh, 100, 1,
|
2303 |
|
|
CMD_XMIT_NOW(START_CHAR(tty)));
|
2304 |
|
|
}
|
2305 |
|
|
break;
|
2306 |
|
|
default:
|
2307 |
|
|
return -EINVAL;
|
2308 |
|
|
}
|
2309 |
|
|
return 0;
|
2310 |
|
|
|
2311 |
|
|
case TCSBRK: /* SVID version: non-zero arg --> no break */
|
2312 |
|
|
rc = tty_check_change(tty);
|
2313 |
|
|
|
2314 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 4, 1, rc );
|
2315 |
|
|
|
2316 |
|
|
if (!rc) {
|
2317 |
|
|
ip2_wait_until_sent(tty,0);
|
2318 |
|
|
if (!arg) {
|
2319 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SEND_BRK(250));
|
2320 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2321 |
|
|
}
|
2322 |
|
|
}
|
2323 |
|
|
break;
|
2324 |
|
|
|
2325 |
|
|
case TCSBRKP: /* support for POSIX tcsendbreak() */
|
2326 |
|
|
rc = tty_check_change(tty);
|
2327 |
|
|
|
2328 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 5, 1, rc );
|
2329 |
|
|
|
2330 |
|
|
if (!rc) {
|
2331 |
|
|
ip2_wait_until_sent(tty,0);
|
2332 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1,
|
2333 |
|
|
CMD_SEND_BRK(arg ? arg*100 : 250));
|
2334 |
|
|
serviceOutgoingFifo ( pCh->pMyBord );
|
2335 |
|
|
}
|
2336 |
|
|
break;
|
2337 |
|
|
|
2338 |
|
|
case TIOCGSOFTCAR:
|
2339 |
|
|
|
2340 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 6, 1, rc );
|
2341 |
|
|
|
2342 |
|
|
PUT_USER(rc,C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg);
|
2343 |
|
|
if (rc)
|
2344 |
|
|
return rc;
|
2345 |
|
|
break;
|
2346 |
|
|
|
2347 |
|
|
case TIOCSSOFTCAR:
|
2348 |
|
|
|
2349 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 7, 1, rc );
|
2350 |
|
|
|
2351 |
|
|
GET_USER(rc,arg,(unsigned long *) arg);
|
2352 |
|
|
if (rc)
|
2353 |
|
|
return rc;
|
2354 |
|
|
tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL)
|
2355 |
|
|
| (arg ? CLOCAL : 0));
|
2356 |
|
|
|
2357 |
|
|
break;
|
2358 |
|
|
|
2359 |
|
|
case TIOCMGET:
|
2360 |
|
|
|
2361 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 8, 1, rc );
|
2362 |
|
|
|
2363 |
|
|
/*
|
2364 |
|
|
FIXME - the following code is causing a NULL pointer dereference in
|
2365 |
|
|
2.3.51 in an interrupt handler. It's suppose to prompt the board
|
2366 |
|
|
to return the DSS signal status immediately. Why doesn't it do
|
2367 |
|
|
the same thing in 2.2.14?
|
2368 |
|
|
*/
|
2369 |
|
|
|
2370 |
|
|
/* This thing is still busted in the 1.2.12 driver on 2.4.x
|
2371 |
|
|
and even hoses the serial console so the oops can be trapped.
|
2372 |
|
|
/\/\|=mhw=|\/\/ */
|
2373 |
|
|
|
2374 |
|
|
#ifdef ENABLE_DSSNOW
|
2375 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DSS_NOW);
|
2376 |
|
|
|
2377 |
|
|
init_waitqueue_entry(&wait, current);
|
2378 |
|
|
add_wait_queue(&pCh->dss_now_wait, &wait);
|
2379 |
|
|
set_current_state( TASK_INTERRUPTIBLE );
|
2380 |
|
|
|
2381 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2382 |
|
|
|
2383 |
|
|
schedule();
|
2384 |
|
|
|
2385 |
|
|
set_current_state( TASK_RUNNING );
|
2386 |
|
|
remove_wait_queue(&pCh->dss_now_wait, &wait);
|
2387 |
|
|
|
2388 |
|
|
if (signal_pending(current)) {
|
2389 |
|
|
return -EINTR;
|
2390 |
|
|
}
|
2391 |
|
|
#endif
|
2392 |
|
|
PUT_USER(rc,
|
2393 |
|
|
((pCh->dataSetOut & I2_RTS) ? TIOCM_RTS : 0)
|
2394 |
|
|
| ((pCh->dataSetOut & I2_DTR) ? TIOCM_DTR : 0)
|
2395 |
|
|
| ((pCh->dataSetIn & I2_DCD) ? TIOCM_CAR : 0)
|
2396 |
|
|
| ((pCh->dataSetIn & I2_RI) ? TIOCM_RNG : 0)
|
2397 |
|
|
| ((pCh->dataSetIn & I2_DSR) ? TIOCM_DSR : 0)
|
2398 |
|
|
| ((pCh->dataSetIn & I2_CTS) ? TIOCM_CTS : 0),
|
2399 |
|
|
(unsigned int *) arg);
|
2400 |
|
|
break;
|
2401 |
|
|
|
2402 |
|
|
case TIOCMBIS:
|
2403 |
|
|
case TIOCMBIC:
|
2404 |
|
|
case TIOCMSET:
|
2405 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 9, 0 );
|
2406 |
|
|
|
2407 |
|
|
rc = set_modem_info(pCh, cmd, (unsigned int *) arg);
|
2408 |
|
|
break;
|
2409 |
|
|
|
2410 |
|
|
/*
|
2411 |
|
|
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change - mask
|
2412 |
|
|
* passed in arg for lines of interest (use |'ed TIOCM_RNG/DSR/CD/CTS
|
2413 |
|
|
* for masking). Caller should use TIOCGICOUNT to see which one it was
|
2414 |
|
|
*/
|
2415 |
|
|
case TIOCMIWAIT:
|
2416 |
|
|
save_flags(flags);cli();
|
2417 |
|
|
cprev = pCh->icount; /* note the counters on entry */
|
2418 |
|
|
restore_flags(flags);
|
2419 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 4,
|
2420 |
|
|
CMD_DCD_REP, CMD_CTS_REP, CMD_DSR_REP, CMD_RI_REP);
|
2421 |
|
|
init_waitqueue_entry(&wait, current);
|
2422 |
|
|
add_wait_queue(&pCh->delta_msr_wait, &wait);
|
2423 |
|
|
set_current_state( TASK_INTERRUPTIBLE );
|
2424 |
|
|
|
2425 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2426 |
|
|
for(;;) {
|
2427 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 10, 0 );
|
2428 |
|
|
|
2429 |
|
|
schedule();
|
2430 |
|
|
|
2431 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 11, 0 );
|
2432 |
|
|
|
2433 |
|
|
/* see if a signal did it */
|
2434 |
|
|
if (signal_pending(current)) {
|
2435 |
|
|
rc = -ERESTARTSYS;
|
2436 |
|
|
break;
|
2437 |
|
|
}
|
2438 |
|
|
save_flags(flags);cli();
|
2439 |
|
|
cnow = pCh->icount; /* atomic copy */
|
2440 |
|
|
restore_flags(flags);
|
2441 |
|
|
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
|
2442 |
|
|
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
|
2443 |
|
|
rc = -EIO; /* no change => rc */
|
2444 |
|
|
break;
|
2445 |
|
|
}
|
2446 |
|
|
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
|
2447 |
|
|
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
|
2448 |
|
|
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
|
2449 |
|
|
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
|
2450 |
|
|
rc = 0;
|
2451 |
|
|
break;
|
2452 |
|
|
}
|
2453 |
|
|
cprev = cnow;
|
2454 |
|
|
}
|
2455 |
|
|
set_current_state( TASK_RUNNING );
|
2456 |
|
|
remove_wait_queue(&pCh->delta_msr_wait, &wait);
|
2457 |
|
|
|
2458 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 3,
|
2459 |
|
|
CMD_CTS_NREP, CMD_DSR_NREP, CMD_RI_NREP);
|
2460 |
|
|
if ( ! (pCh->flags & ASYNC_CHECK_CD)) {
|
2461 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DCD_NREP);
|
2462 |
|
|
}
|
2463 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2464 |
|
|
return rc;
|
2465 |
|
|
break;
|
2466 |
|
|
|
2467 |
|
|
/*
|
2468 |
|
|
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
|
2469 |
|
|
* Return: write counters to the user passed counter struct
|
2470 |
|
|
* NB: both 1->0 and 0->1 transitions are counted except for RI where
|
2471 |
|
|
* only 0->1 is counted. The controller is quite capable of counting
|
2472 |
|
|
* both, but this done to preserve compatibility with the standard
|
2473 |
|
|
* serial driver.
|
2474 |
|
|
*/
|
2475 |
|
|
case TIOCGICOUNT:
|
2476 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 11, 1, rc );
|
2477 |
|
|
|
2478 |
|
|
save_flags(flags);cli();
|
2479 |
|
|
cnow = pCh->icount;
|
2480 |
|
|
restore_flags(flags);
|
2481 |
|
|
p_cuser = (struct serial_icounter_struct *) arg;
|
2482 |
|
|
PUT_USER(rc,cnow.cts, &p_cuser->cts);
|
2483 |
|
|
PUT_USER(rc,cnow.dsr, &p_cuser->dsr);
|
2484 |
|
|
PUT_USER(rc,cnow.rng, &p_cuser->rng);
|
2485 |
|
|
PUT_USER(rc,cnow.dcd, &p_cuser->dcd);
|
2486 |
|
|
PUT_USER(rc,cnow.rx, &p_cuser->rx);
|
2487 |
|
|
PUT_USER(rc,cnow.tx, &p_cuser->tx);
|
2488 |
|
|
PUT_USER(rc,cnow.frame, &p_cuser->frame);
|
2489 |
|
|
PUT_USER(rc,cnow.overrun, &p_cuser->overrun);
|
2490 |
|
|
PUT_USER(rc,cnow.parity, &p_cuser->parity);
|
2491 |
|
|
PUT_USER(rc,cnow.brk, &p_cuser->brk);
|
2492 |
|
|
PUT_USER(rc,cnow.buf_overrun, &p_cuser->buf_overrun);
|
2493 |
|
|
break;
|
2494 |
|
|
|
2495 |
|
|
/*
|
2496 |
|
|
* The rest are not supported by this driver. By returning -ENOIOCTLCMD they
|
2497 |
|
|
* will be passed to the line discipline for it to handle.
|
2498 |
|
|
*/
|
2499 |
|
|
case TIOCSERCONFIG:
|
2500 |
|
|
case TIOCSERGWILD:
|
2501 |
|
|
case TIOCSERGETLSR:
|
2502 |
|
|
case TIOCSERSWILD:
|
2503 |
|
|
case TIOCSERGSTRUCT:
|
2504 |
|
|
case TIOCSERGETMULTI:
|
2505 |
|
|
case TIOCSERSETMULTI:
|
2506 |
|
|
|
2507 |
|
|
default:
|
2508 |
|
|
ip2trace (CHANN, ITRC_IOCTL, 12, 0 );
|
2509 |
|
|
|
2510 |
|
|
rc = -ENOIOCTLCMD;
|
2511 |
|
|
break;
|
2512 |
|
|
}
|
2513 |
|
|
|
2514 |
|
|
ip2trace (CHANN, ITRC_IOCTL, ITRC_RETURN, 0 );
|
2515 |
|
|
|
2516 |
|
|
return rc;
|
2517 |
|
|
}
|
2518 |
|
|
|
2519 |
|
|
/******************************************************************************/
|
2520 |
|
|
/* Function: set_modem_info() */
|
2521 |
|
|
/* Parameters: Pointer to channel structure */
|
2522 |
|
|
/* Specific ioctl command */
|
2523 |
|
|
/* Pointer to source for new settings */
|
2524 |
|
|
/* Returns: Nothing */
|
2525 |
|
|
/* */
|
2526 |
|
|
/* Description: */
|
2527 |
|
|
/* This returns the current settings of the dataset signal inputs to the user */
|
2528 |
|
|
/* program. */
|
2529 |
|
|
/******************************************************************************/
|
2530 |
|
|
static int
|
2531 |
|
|
set_modem_info(i2ChanStrPtr pCh, unsigned cmd, unsigned int *value)
|
2532 |
|
|
{
|
2533 |
|
|
int rc;
|
2534 |
|
|
unsigned int arg;
|
2535 |
|
|
|
2536 |
|
|
GET_USER(rc,arg,value);
|
2537 |
|
|
if (rc)
|
2538 |
|
|
return rc;
|
2539 |
|
|
switch(cmd) {
|
2540 |
|
|
case TIOCMBIS:
|
2541 |
|
|
if (arg & TIOCM_RTS) {
|
2542 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP);
|
2543 |
|
|
pCh->dataSetOut |= I2_RTS;
|
2544 |
|
|
}
|
2545 |
|
|
if (arg & TIOCM_DTR) {
|
2546 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP);
|
2547 |
|
|
pCh->dataSetOut |= I2_DTR;
|
2548 |
|
|
}
|
2549 |
|
|
break;
|
2550 |
|
|
case TIOCMBIC:
|
2551 |
|
|
if (arg & TIOCM_RTS) {
|
2552 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN);
|
2553 |
|
|
pCh->dataSetOut &= ~I2_RTS;
|
2554 |
|
|
}
|
2555 |
|
|
if (arg & TIOCM_DTR) {
|
2556 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN);
|
2557 |
|
|
pCh->dataSetOut &= ~I2_DTR;
|
2558 |
|
|
}
|
2559 |
|
|
break;
|
2560 |
|
|
case TIOCMSET:
|
2561 |
|
|
if ( (arg & TIOCM_RTS) && !(pCh->dataSetOut & I2_RTS) ) {
|
2562 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSUP);
|
2563 |
|
|
pCh->dataSetOut |= I2_RTS;
|
2564 |
|
|
} else if ( !(arg & TIOCM_RTS) && (pCh->dataSetOut & I2_RTS) ) {
|
2565 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_RTSDN);
|
2566 |
|
|
pCh->dataSetOut &= ~I2_RTS;
|
2567 |
|
|
}
|
2568 |
|
|
if ( (arg & TIOCM_DTR) && !(pCh->dataSetOut & I2_DTR) ) {
|
2569 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRUP);
|
2570 |
|
|
pCh->dataSetOut |= I2_DTR;
|
2571 |
|
|
} else if ( !(arg & TIOCM_DTR) && (pCh->dataSetOut & I2_DTR) ) {
|
2572 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DTRDN);
|
2573 |
|
|
pCh->dataSetOut &= ~I2_DTR;
|
2574 |
|
|
}
|
2575 |
|
|
break;
|
2576 |
|
|
default:
|
2577 |
|
|
return -EINVAL;
|
2578 |
|
|
}
|
2579 |
|
|
serviceOutgoingFifo( pCh->pMyBord );
|
2580 |
|
|
return 0;
|
2581 |
|
|
}
|
2582 |
|
|
|
2583 |
|
|
/******************************************************************************/
|
2584 |
|
|
/* Function: GetSerialInfo() */
|
2585 |
|
|
/* Parameters: Pointer to channel structure */
|
2586 |
|
|
/* Pointer to old termios structure */
|
2587 |
|
|
/* Returns: Nothing */
|
2588 |
|
|
/* */
|
2589 |
|
|
/* Description: */
|
2590 |
|
|
/* This is to support the setserial command, and requires processing of the */
|
2591 |
|
|
/* standard Linux serial structure. */
|
2592 |
|
|
/******************************************************************************/
|
2593 |
|
|
static int
|
2594 |
|
|
get_serial_info ( i2ChanStrPtr pCh, struct serial_struct *retinfo )
|
2595 |
|
|
{
|
2596 |
|
|
struct serial_struct tmp;
|
2597 |
|
|
int rc;
|
2598 |
|
|
|
2599 |
|
|
if ( !retinfo ) {
|
2600 |
|
|
return -EFAULT;
|
2601 |
|
|
}
|
2602 |
|
|
|
2603 |
|
|
memset ( &tmp, 0, sizeof(tmp) );
|
2604 |
|
|
tmp.type = pCh->pMyBord->channelBtypes.bid_value[(pCh->port_index & (IP2_PORTS_PER_BOARD-1))/16];
|
2605 |
|
|
if (BID_HAS_654(tmp.type)) {
|
2606 |
|
|
tmp.type = PORT_16650;
|
2607 |
|
|
} else {
|
2608 |
|
|
tmp.type = PORT_CIRRUS;
|
2609 |
|
|
}
|
2610 |
|
|
tmp.line = pCh->port_index;
|
2611 |
|
|
tmp.port = pCh->pMyBord->i2eBase;
|
2612 |
|
|
tmp.irq = ip2config.irq[pCh->port_index/64];
|
2613 |
|
|
tmp.flags = pCh->flags;
|
2614 |
|
|
tmp.baud_base = pCh->BaudBase;
|
2615 |
|
|
tmp.close_delay = pCh->ClosingDelay;
|
2616 |
|
|
tmp.closing_wait = pCh->ClosingWaitTime;
|
2617 |
|
|
tmp.custom_divisor = pCh->BaudDivisor;
|
2618 |
|
|
COPY_TO_USER(rc,retinfo,&tmp,sizeof(*retinfo));
|
2619 |
|
|
return rc;
|
2620 |
|
|
}
|
2621 |
|
|
|
2622 |
|
|
/******************************************************************************/
|
2623 |
|
|
/* Function: SetSerialInfo() */
|
2624 |
|
|
/* Parameters: Pointer to channel structure */
|
2625 |
|
|
/* Pointer to old termios structure */
|
2626 |
|
|
/* Returns: Nothing */
|
2627 |
|
|
/* */
|
2628 |
|
|
/* Description: */
|
2629 |
|
|
/* This function provides support for setserial, which uses the TIOCSSERIAL */
|
2630 |
|
|
/* ioctl. Not all setserial parameters are relevant. If the user attempts to */
|
2631 |
|
|
/* change the IRQ, address or type of the port the ioctl fails. */
|
2632 |
|
|
/******************************************************************************/
|
2633 |
|
|
static int
|
2634 |
|
|
set_serial_info( i2ChanStrPtr pCh, struct serial_struct *new_info )
|
2635 |
|
|
{
|
2636 |
|
|
struct serial_struct ns;
|
2637 |
|
|
int old_flags, old_baud_divisor;
|
2638 |
|
|
int rc = 0;
|
2639 |
|
|
|
2640 |
|
|
if ( !new_info ) {
|
2641 |
|
|
return -EFAULT;
|
2642 |
|
|
}
|
2643 |
|
|
COPY_FROM_USER(rc, &ns, new_info, sizeof (ns) );
|
2644 |
|
|
if (rc) {
|
2645 |
|
|
return rc;
|
2646 |
|
|
}
|
2647 |
|
|
/*
|
2648 |
|
|
* We don't allow setserial to change IRQ, board address, type or baud
|
2649 |
|
|
* base. Also line nunber as such is meaningless but we use it for our
|
2650 |
|
|
* array index so it is fixed also.
|
2651 |
|
|
*/
|
2652 |
|
|
if ( (ns.irq != ip2config.irq[pCh->port_index])
|
2653 |
|
|
|| ((int) ns.port != ((int) (pCh->pMyBord->i2eBase)))
|
2654 |
|
|
|| (ns.baud_base != pCh->BaudBase)
|
2655 |
|
|
|| (ns.line != pCh->port_index) ) {
|
2656 |
|
|
return -EINVAL;
|
2657 |
|
|
}
|
2658 |
|
|
|
2659 |
|
|
old_flags = pCh->flags;
|
2660 |
|
|
old_baud_divisor = pCh->BaudDivisor;
|
2661 |
|
|
|
2662 |
|
|
if ( !suser() ) {
|
2663 |
|
|
if ( ( ns.close_delay != pCh->ClosingDelay ) ||
|
2664 |
|
|
( (ns.flags & ~ASYNC_USR_MASK) !=
|
2665 |
|
|
(pCh->flags & ~ASYNC_USR_MASK) ) ) {
|
2666 |
|
|
return -EPERM;
|
2667 |
|
|
}
|
2668 |
|
|
|
2669 |
|
|
pCh->flags = (pCh->flags & ~ASYNC_USR_MASK) |
|
2670 |
|
|
(ns.flags & ASYNC_USR_MASK);
|
2671 |
|
|
pCh->BaudDivisor = ns.custom_divisor;
|
2672 |
|
|
} else {
|
2673 |
|
|
pCh->flags = (pCh->flags & ~ASYNC_FLAGS) |
|
2674 |
|
|
(ns.flags & ASYNC_FLAGS);
|
2675 |
|
|
pCh->BaudDivisor = ns.custom_divisor;
|
2676 |
|
|
pCh->ClosingDelay = ns.close_delay * HZ/100;
|
2677 |
|
|
pCh->ClosingWaitTime = ns.closing_wait * HZ/100;
|
2678 |
|
|
}
|
2679 |
|
|
|
2680 |
|
|
if ( ( (old_flags & ASYNC_SPD_MASK) != (pCh->flags & ASYNC_SPD_MASK) )
|
2681 |
|
|
|| (old_baud_divisor != pCh->BaudDivisor) ) {
|
2682 |
|
|
// Invalidate speed and reset parameters
|
2683 |
|
|
set_params( pCh, NULL );
|
2684 |
|
|
}
|
2685 |
|
|
|
2686 |
|
|
return rc;
|
2687 |
|
|
}
|
2688 |
|
|
|
2689 |
|
|
/******************************************************************************/
|
2690 |
|
|
/* Function: ip2_set_termios() */
|
2691 |
|
|
/* Parameters: Pointer to tty structure */
|
2692 |
|
|
/* Pointer to old termios structure */
|
2693 |
|
|
/* Returns: Nothing */
|
2694 |
|
|
/* */
|
2695 |
|
|
/* Description: */
|
2696 |
|
|
/* */
|
2697 |
|
|
/* */
|
2698 |
|
|
/******************************************************************************/
|
2699 |
|
|
static void
|
2700 |
|
|
ip2_set_termios( PTTY tty, struct termios *old_termios )
|
2701 |
|
|
{
|
2702 |
|
|
i2ChanStrPtr pCh = (i2ChanStrPtr)tty->driver_data;
|
2703 |
|
|
|
2704 |
|
|
#ifdef IP2DEBUG_IOCTL
|
2705 |
|
|
printk (KERN_DEBUG "IP2: set termios %p\n", old_termios );
|
2706 |
|
|
#endif
|
2707 |
|
|
|
2708 |
|
|
set_params( pCh, old_termios );
|
2709 |
|
|
}
|
2710 |
|
|
|
2711 |
|
|
/******************************************************************************/
|
2712 |
|
|
/* Function: ip2_set_line_discipline() */
|
2713 |
|
|
/* Parameters: Pointer to tty structure */
|
2714 |
|
|
/* Returns: Nothing */
|
2715 |
|
|
/* */
|
2716 |
|
|
/* Description: Does nothing */
|
2717 |
|
|
/* */
|
2718 |
|
|
/* */
|
2719 |
|
|
/******************************************************************************/
|
2720 |
|
|
static void
|
2721 |
|
|
ip2_set_line_discipline ( PTTY tty )
|
2722 |
|
|
{
|
2723 |
|
|
#ifdef IP2DEBUG_IOCTL
|
2724 |
|
|
printk (KERN_DEBUG "IP2: set line discipline\n" );
|
2725 |
|
|
#endif
|
2726 |
|
|
|
2727 |
|
|
ip2trace (((i2ChanStrPtr)tty->driver_data)->port_index, ITRC_IOCTL, 16, 0 );
|
2728 |
|
|
|
2729 |
|
|
}
|
2730 |
|
|
|
2731 |
|
|
/******************************************************************************/
|
2732 |
|
|
/* Function: SetLine Characteristics() */
|
2733 |
|
|
/* Parameters: Pointer to channel structure */
|
2734 |
|
|
/* Returns: Nothing */
|
2735 |
|
|
/* */
|
2736 |
|
|
/* Description: */
|
2737 |
|
|
/* This routine is called to update the channel structure with the new line */
|
2738 |
|
|
/* characteristics, and send the appropriate commands to the board when they */
|
2739 |
|
|
/* change. */
|
2740 |
|
|
/******************************************************************************/
|
2741 |
|
|
static void
|
2742 |
|
|
set_params( i2ChanStrPtr pCh, struct termios *o_tios )
|
2743 |
|
|
{
|
2744 |
|
|
tcflag_t cflag, iflag, lflag;
|
2745 |
|
|
char stop_char, start_char;
|
2746 |
|
|
struct termios dummy;
|
2747 |
|
|
|
2748 |
|
|
lflag = pCh->pTTY->termios->c_lflag;
|
2749 |
|
|
cflag = pCh->pTTY->termios->c_cflag;
|
2750 |
|
|
iflag = pCh->pTTY->termios->c_iflag;
|
2751 |
|
|
|
2752 |
|
|
if (o_tios == NULL) {
|
2753 |
|
|
dummy.c_lflag = ~lflag;
|
2754 |
|
|
dummy.c_cflag = ~cflag;
|
2755 |
|
|
dummy.c_iflag = ~iflag;
|
2756 |
|
|
o_tios = &dummy;
|
2757 |
|
|
}
|
2758 |
|
|
|
2759 |
|
|
{
|
2760 |
|
|
switch ( cflag & CBAUD ) {
|
2761 |
|
|
case B0:
|
2762 |
|
|
i2QueueCommands( PTYPE_BYPASS, pCh, 100, 2, CMD_RTSDN, CMD_DTRDN);
|
2763 |
|
|
pCh->dataSetOut &= ~(I2_DTR | I2_RTS);
|
2764 |
|
|
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_PAUSE(25));
|
2765 |
|
|
pCh->pTTY->termios->c_cflag |= (CBAUD & o_tios->c_cflag);
|
2766 |
|
|
goto service_it;
|
2767 |
|
|
break;
|
2768 |
|
|
case B38400:
|
2769 |
|
|
/*
|
2770 |
|
|
* This is the speed that is overloaded with all the other high
|
2771 |
|
|
* speeds, depending upon the flag settings.
|
2772 |
|
|
*/
|
2773 |
|
|
if ( ( pCh->flags & ASYNC_SPD_MASK ) == ASYNC_SPD_HI ) {
|
2774 |
|
|
pCh->speed = CBR_57600;
|
2775 |
|
|
} else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI ) {
|
2776 |
|
|
pCh->speed = CBR_115200;
|
2777 |
|
|
} else if ( (pCh->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST ) {
|
2778 |
|
|
pCh->speed = CBR_C1;
|
2779 |
|
|
} else {
|
2780 |
|
|
pCh->speed = CBR_38400;
|
2781 |
|
|
}
|
2782 |
|
|
break;
|
2783 |
|
|
case B50: pCh->speed = CBR_50; break;
|
2784 |
|
|
case B75: pCh->speed = CBR_75; break;
|
2785 |
|
|
case B110: pCh->speed = CBR_110; break;
|
2786 |
|
|
case B134: pCh->speed = CBR_134; break;
|
2787 |
|
|
case B150: pCh->speed = CBR_150; break;
|
2788 |
|
|
case B200: pCh->speed = CBR_200; break;
|
2789 |
|
|
case B300: pCh->speed = CBR_300; break;
|
2790 |
|
|
case B600: pCh->speed = CBR_600; break;
|
2791 |
|
|
case B1200: pCh->speed = CBR_1200; break;
|
2792 |
|
|
case B1800: pCh->speed = CBR_1800; break;
|
2793 |
|
|
case B2400: pCh->speed = CBR_2400; break;
|
2794 |
|
|
case B4800: pCh->speed = CBR_4800; break;
|
2795 |
|
|
case B9600: pCh->speed = CBR_9600; break;
|
2796 |
|
|
case B19200: pCh->speed = CBR_19200; break;
|
2797 |
|
|
case B57600: pCh->speed = CBR_57600; break;
|
2798 |
|
|
case B115200: pCh->speed = CBR_115200; break;
|
2799 |
|
|
case B153600: pCh->speed = CBR_153600; break;
|
2800 |
|
|
case B230400: pCh->speed = CBR_230400; break;
|
2801 |
|
|
case B307200: pCh->speed = CBR_307200; break;
|
2802 |
|
|
case B460800: pCh->speed = CBR_460800; break;
|
2803 |
|
|
case B921600: pCh->speed = CBR_921600; break;
|
2804 |
|
|
default: pCh->speed = CBR_9600; break;
|
2805 |
|
|
}
|
2806 |
|
|
if ( pCh->speed == CBR_C1 ) {
|
2807 |
|
|
// Process the custom speed parameters.
|
2808 |
|
|
int bps = pCh->BaudBase / pCh->BaudDivisor;
|
2809 |
|
|
if ( bps == 921600 ) {
|
2810 |
|
|
pCh->speed = CBR_921600;
|
2811 |
|
|
} else {
|
2812 |
|
|
bps = bps/10;
|
2813 |
|
|
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_BAUD_DEF1(bps) );
|
2814 |
|
|
}
|
2815 |
|
|
}
|
2816 |
|
|
i2QueueCommands( PTYPE_INLINE, pCh, 100, 1, CMD_SETBAUD(pCh->speed));
|
2817 |
|
|
|
2818 |
|
|
i2QueueCommands ( PTYPE_INLINE, pCh, 100, 2, CMD_DTRUP, CMD_RTSUP);
|
2819 |
|
|
pCh->dataSetOut |= (I2_DTR | I2_RTS);
|
2820 |
|
|
}
|
2821 |
|
|
if ( (CSTOPB & cflag) ^ (CSTOPB & o_tios->c_cflag))
|
2822 |
|
|
{
|
2823 |
|
|
i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1,
|
2824 |
|
|
CMD_SETSTOP( ( cflag & CSTOPB ) ? CST_2 : CST_1));
|
2825 |
|
|
}
|
2826 |
|
|
if (((PARENB|PARODD) & cflag) ^ ((PARENB|PARODD) & o_tios->c_cflag))
|
2827 |
|
|
{
|
2828 |
|
|
i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1,
|
2829 |
|
|
CMD_SETPAR(
|
2830 |
|
|
(cflag & PARENB ? (cflag & PARODD ? CSP_OD : CSP_EV) : CSP_NP)
|
2831 |
|
|
)
|
2832 |
|
|
);
|
2833 |
|
|
}
|
2834 |
|
|
/* byte size and parity */
|
2835 |
|
|
if ( (CSIZE & cflag)^(CSIZE & o_tios->c_cflag))
|
2836 |
|
|
{
|
2837 |
|
|
int datasize;
|
2838 |
|
|
switch ( cflag & CSIZE ) {
|
2839 |
|
|
case CS5: datasize = CSZ_5; break;
|
2840 |
|
|
case CS6: datasize = CSZ_6; break;
|
2841 |
|
|
case CS7: datasize = CSZ_7; break;
|
2842 |
|
|
case CS8: datasize = CSZ_8; break;
|
2843 |
|
|
default: datasize = CSZ_5; break; /* as per serial.c */
|
2844 |
|
|
}
|
2845 |
|
|
i2QueueCommands ( PTYPE_INLINE, pCh, 100, 1, CMD_SETBITS(datasize) );
|
2846 |
|
|
}
|
2847 |
|
|
/* Process CTS flow control flag setting */
|
2848 |
|
|
if ( (cflag & CRTSCTS) ) {
|
2849 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100,
|
2850 |
|
|
2, CMD_CTSFL_ENAB, CMD_RTSFL_ENAB);
|
2851 |
|
|
} else {
|
2852 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100,
|
2853 |
|
|
2, CMD_CTSFL_DSAB, CMD_RTSFL_DSAB);
|
2854 |
|
|
}
|
2855 |
|
|
//
|
2856 |
|
|
// Process XON/XOFF flow control flags settings
|
2857 |
|
|
//
|
2858 |
|
|
stop_char = STOP_CHAR(pCh->pTTY);
|
2859 |
|
|
start_char = START_CHAR(pCh->pTTY);
|
2860 |
|
|
|
2861 |
|
|
//////////// can't be \000
|
2862 |
|
|
if (stop_char == __DISABLED_CHAR )
|
2863 |
|
|
{
|
2864 |
|
|
stop_char = ~__DISABLED_CHAR;
|
2865 |
|
|
}
|
2866 |
|
|
if (start_char == __DISABLED_CHAR )
|
2867 |
|
|
{
|
2868 |
|
|
start_char = ~__DISABLED_CHAR;
|
2869 |
|
|
}
|
2870 |
|
|
/////////////////////////////////
|
2871 |
|
|
|
2872 |
|
|
if ( o_tios->c_cc[VSTART] != start_char )
|
2873 |
|
|
{
|
2874 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXON(start_char));
|
2875 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXON(start_char));
|
2876 |
|
|
}
|
2877 |
|
|
if ( o_tios->c_cc[VSTOP] != stop_char )
|
2878 |
|
|
{
|
2879 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1, CMD_DEF_IXOFF(stop_char));
|
2880 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DEF_OXOFF(stop_char));
|
2881 |
|
|
}
|
2882 |
|
|
if (stop_char == __DISABLED_CHAR )
|
2883 |
|
|
{
|
2884 |
|
|
stop_char = ~__DISABLED_CHAR; //TEST123
|
2885 |
|
|
goto no_xoff;
|
2886 |
|
|
}
|
2887 |
|
|
if ((iflag & (IXOFF))^(o_tios->c_iflag & (IXOFF)))
|
2888 |
|
|
{
|
2889 |
|
|
if ( iflag & IXOFF ) { // Enable XOFF output flow control
|
2890 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_XON));
|
2891 |
|
|
} else { // Disable XOFF output flow control
|
2892 |
|
|
no_xoff:
|
2893 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_OXON_OPT(COX_NONE));
|
2894 |
|
|
}
|
2895 |
|
|
}
|
2896 |
|
|
if (start_char == __DISABLED_CHAR )
|
2897 |
|
|
{
|
2898 |
|
|
goto no_xon;
|
2899 |
|
|
}
|
2900 |
|
|
if ((iflag & (IXON|IXANY)) ^ (o_tios->c_iflag & (IXON|IXANY)))
|
2901 |
|
|
{
|
2902 |
|
|
if ( iflag & IXON ) {
|
2903 |
|
|
if ( iflag & IXANY ) { // Enable XON/XANY output flow control
|
2904 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XANY));
|
2905 |
|
|
} else { // Enable XON output flow control
|
2906 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_XON));
|
2907 |
|
|
}
|
2908 |
|
|
} else { // Disable XON output flow control
|
2909 |
|
|
no_xon:
|
2910 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_IXON_OPT(CIX_NONE));
|
2911 |
|
|
}
|
2912 |
|
|
}
|
2913 |
|
|
if ( (iflag & ISTRIP) ^ ( o_tios->c_iflag & (ISTRIP)) )
|
2914 |
|
|
{
|
2915 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1,
|
2916 |
|
|
CMD_ISTRIP_OPT((iflag & ISTRIP ? 1 : 0)));
|
2917 |
|
|
}
|
2918 |
|
|
if ( (iflag & INPCK) ^ ( o_tios->c_iflag & (INPCK)) )
|
2919 |
|
|
{
|
2920 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1,
|
2921 |
|
|
CMD_PARCHK((iflag & INPCK) ? CPK_ENAB : CPK_DSAB));
|
2922 |
|
|
}
|
2923 |
|
|
|
2924 |
|
|
if ( (iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR))
|
2925 |
|
|
^ ( o_tios->c_iflag & (IGNBRK|PARMRK|BRKINT|IGNPAR)) )
|
2926 |
|
|
{
|
2927 |
|
|
char brkrpt = 0;
|
2928 |
|
|
char parrpt = 0;
|
2929 |
|
|
|
2930 |
|
|
if ( iflag & IGNBRK ) { /* Ignore breaks altogether */
|
2931 |
|
|
/* Ignore breaks altogether */
|
2932 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_NREP);
|
2933 |
|
|
} else {
|
2934 |
|
|
if ( iflag & BRKINT ) {
|
2935 |
|
|
if ( iflag & PARMRK ) {
|
2936 |
|
|
brkrpt = 0x0a; // exception an inline triple
|
2937 |
|
|
} else {
|
2938 |
|
|
brkrpt = 0x1a; // exception and NULL
|
2939 |
|
|
}
|
2940 |
|
|
brkrpt |= 0x04; // flush input
|
2941 |
|
|
} else {
|
2942 |
|
|
if ( iflag & PARMRK ) {
|
2943 |
|
|
brkrpt = 0x0b; //POSIX triple \0377 \0 \0
|
2944 |
|
|
} else {
|
2945 |
|
|
brkrpt = 0x01; // Null only
|
2946 |
|
|
}
|
2947 |
|
|
}
|
2948 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_BRK_REP(brkrpt));
|
2949 |
|
|
}
|
2950 |
|
|
|
2951 |
|
|
if (iflag & IGNPAR) {
|
2952 |
|
|
parrpt = 0x20;
|
2953 |
|
|
/* would be 2 for not cirrus bug */
|
2954 |
|
|
/* would be 0x20 cept for cirrus bug */
|
2955 |
|
|
} else {
|
2956 |
|
|
if ( iflag & PARMRK ) {
|
2957 |
|
|
/*
|
2958 |
|
|
* Replace error characters with 3-byte sequence (\0377,\0,char)
|
2959 |
|
|
*/
|
2960 |
|
|
parrpt = 0x04 ;
|
2961 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_ISTRIP_OPT((char)0));
|
2962 |
|
|
} else {
|
2963 |
|
|
parrpt = 0x03;
|
2964 |
|
|
}
|
2965 |
|
|
}
|
2966 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_SET_ERROR(parrpt));
|
2967 |
|
|
}
|
2968 |
|
|
if (cflag & CLOCAL) {
|
2969 |
|
|
// Status reporting fails for DCD if this is off
|
2970 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_NREP);
|
2971 |
|
|
pCh->flags &= ~ASYNC_CHECK_CD;
|
2972 |
|
|
} else {
|
2973 |
|
|
i2QueueCommands(PTYPE_INLINE, pCh, 100, 1, CMD_DCD_REP);
|
2974 |
|
|
pCh->flags |= ASYNC_CHECK_CD;
|
2975 |
|
|
}
|
2976 |
|
|
|
2977 |
|
|
#ifdef XXX
|
2978 |
|
|
do_flags_thing: // This is a test, we don't do the flags thing
|
2979 |
|
|
|
2980 |
|
|
if ( (cflag & CRTSCTS) ) {
|
2981 |
|
|
cflag |= 014000000000;
|
2982 |
|
|
}
|
2983 |
|
|
i2QueueCommands(PTYPE_BYPASS, pCh, 100, 1,
|
2984 |
|
|
CMD_UNIX_FLAGS(iflag,cflag,lflag));
|
2985 |
|
|
#endif
|
2986 |
|
|
|
2987 |
|
|
service_it:
|
2988 |
|
|
i2DrainOutput( pCh, 100 );
|
2989 |
|
|
}
|
2990 |
|
|
|
2991 |
|
|
/******************************************************************************/
|
2992 |
|
|
/* IPL Device Section */
|
2993 |
|
|
/******************************************************************************/
|
2994 |
|
|
|
2995 |
|
|
/******************************************************************************/
|
2996 |
|
|
/* Function: ip2_ipl_read() */
|
2997 |
|
|
/* Parameters: Pointer to device inode */
|
2998 |
|
|
/* Pointer to file structure */
|
2999 |
|
|
/* Pointer to data */
|
3000 |
|
|
/* Number of bytes to read */
|
3001 |
|
|
/* Returns: Success or failure */
|
3002 |
|
|
/* */
|
3003 |
|
|
/* Description: Ugly */
|
3004 |
|
|
/* */
|
3005 |
|
|
/* */
|
3006 |
|
|
/******************************************************************************/
|
3007 |
|
|
|
3008 |
|
|
static
|
3009 |
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
|
3010 |
|
|
int
|
3011 |
|
|
ip2_ipl_read(struct inode *pInode, char *pData, size_t count, loff_t *off )
|
3012 |
|
|
unsigned int minor = MINOR( pInode->i_rdev );
|
3013 |
|
|
#else
|
3014 |
|
|
ssize_t
|
3015 |
|
|
ip2_ipl_read(struct file *pFile, char *pData, size_t count, loff_t *off )
|
3016 |
|
|
{
|
3017 |
|
|
unsigned int minor = MINOR( pFile->f_dentry->d_inode->i_rdev );
|
3018 |
|
|
#endif
|
3019 |
|
|
int rc = 0;
|
3020 |
|
|
|
3021 |
|
|
#ifdef IP2DEBUG_IPL
|
3022 |
|
|
printk (KERN_DEBUG "IP2IPL: read %p, %d bytes\n", pData, count );
|
3023 |
|
|
#endif
|
3024 |
|
|
|
3025 |
|
|
switch( minor ) {
|
3026 |
|
|
case 0: // IPL device
|
3027 |
|
|
rc = -EINVAL;
|
3028 |
|
|
break;
|
3029 |
|
|
case 1: // Status dump
|
3030 |
|
|
rc = -EINVAL;
|
3031 |
|
|
break;
|
3032 |
|
|
case 2: // Ping device
|
3033 |
|
|
rc = -EINVAL;
|
3034 |
|
|
break;
|
3035 |
|
|
case 3: // Trace device
|
3036 |
|
|
rc = DumpTraceBuffer ( pData, count );
|
3037 |
|
|
break;
|
3038 |
|
|
case 4: // Trace device
|
3039 |
|
|
rc = DumpFifoBuffer ( pData, count );
|
3040 |
|
|
break;
|
3041 |
|
|
default:
|
3042 |
|
|
rc = -ENODEV;
|
3043 |
|
|
break;
|
3044 |
|
|
}
|
3045 |
|
|
return rc;
|
3046 |
|
|
}
|
3047 |
|
|
|
3048 |
|
|
static int
|
3049 |
|
|
DumpFifoBuffer ( char *pData, int count )
|
3050 |
|
|
{
|
3051 |
|
|
#ifdef DEBUG_FIFO
|
3052 |
|
|
int rc;
|
3053 |
|
|
COPY_TO_USER(rc, pData, DBGBuf, count);
|
3054 |
|
|
|
3055 |
|
|
printk(KERN_DEBUG "Last index %d\n", I );
|
3056 |
|
|
|
3057 |
|
|
return count;
|
3058 |
|
|
#endif /* DEBUG_FIFO */
|
3059 |
|
|
return 0;
|
3060 |
|
|
}
|
3061 |
|
|
|
3062 |
|
|
static int
|
3063 |
|
|
DumpTraceBuffer ( char *pData, int count )
|
3064 |
|
|
{
|
3065 |
|
|
#ifdef IP2DEBUG_TRACE
|
3066 |
|
|
int rc;
|
3067 |
|
|
int dumpcount;
|
3068 |
|
|
int chunk;
|
3069 |
|
|
int *pIndex = (int*)pData;
|
3070 |
|
|
|
3071 |
|
|
if ( count < (sizeof(int) * 6) ) {
|
3072 |
|
|
return -EIO;
|
3073 |
|
|
}
|
3074 |
|
|
PUT_USER(rc, tracewrap, pIndex );
|
3075 |
|
|
PUT_USER(rc, TRACEMAX, ++pIndex );
|
3076 |
|
|
PUT_USER(rc, tracestrip, ++pIndex );
|
3077 |
|
|
PUT_USER(rc, tracestuff, ++pIndex );
|
3078 |
|
|
pData += sizeof(int) * 6;
|
3079 |
|
|
count -= sizeof(int) * 6;
|
3080 |
|
|
|
3081 |
|
|
dumpcount = tracestuff - tracestrip;
|
3082 |
|
|
if ( dumpcount < 0 ) {
|
3083 |
|
|
dumpcount += TRACEMAX;
|
3084 |
|
|
}
|
3085 |
|
|
if ( dumpcount > count ) {
|
3086 |
|
|
dumpcount = count;
|
3087 |
|
|
}
|
3088 |
|
|
chunk = TRACEMAX - tracestrip;
|
3089 |
|
|
if ( dumpcount > chunk ) {
|
3090 |
|
|
COPY_TO_USER(rc, pData, &tracebuf[tracestrip],
|
3091 |
|
|
chunk * sizeof(tracebuf[0]) );
|
3092 |
|
|
pData += chunk * sizeof(tracebuf[0]);
|
3093 |
|
|
tracestrip = 0;
|
3094 |
|
|
chunk = dumpcount - chunk;
|
3095 |
|
|
} else {
|
3096 |
|
|
chunk = dumpcount;
|
3097 |
|
|
}
|
3098 |
|
|
COPY_TO_USER(rc, pData, &tracebuf[tracestrip],
|
3099 |
|
|
chunk * sizeof(tracebuf[0]) );
|
3100 |
|
|
tracestrip += chunk;
|
3101 |
|
|
tracewrap = 0;
|
3102 |
|
|
|
3103 |
|
|
PUT_USER(rc, tracestrip, ++pIndex );
|
3104 |
|
|
PUT_USER(rc, tracestuff, ++pIndex );
|
3105 |
|
|
|
3106 |
|
|
return dumpcount;
|
3107 |
|
|
#else
|
3108 |
|
|
return 0;
|
3109 |
|
|
#endif
|
3110 |
|
|
}
|
3111 |
|
|
|
3112 |
|
|
/******************************************************************************/
|
3113 |
|
|
/* Function: ip2_ipl_write() */
|
3114 |
|
|
/* Parameters: */
|
3115 |
|
|
/* Pointer to file structure */
|
3116 |
|
|
/* Pointer to data */
|
3117 |
|
|
/* Number of bytes to write */
|
3118 |
|
|
/* Returns: Success or failure */
|
3119 |
|
|
/* */
|
3120 |
|
|
/* Description: */
|
3121 |
|
|
/* */
|
3122 |
|
|
/* */
|
3123 |
|
|
/******************************************************************************/
|
3124 |
|
|
static ssize_t
|
3125 |
|
|
ip2_ipl_write(struct file *pFile, const char *pData, size_t count, loff_t *off)
|
3126 |
|
|
{
|
3127 |
|
|
#ifdef IP2DEBUG_IPL
|
3128 |
|
|
printk (KERN_DEBUG "IP2IPL: write %p, %d bytes\n", pData, count );
|
3129 |
|
|
#endif
|
3130 |
|
|
return 0;
|
3131 |
|
|
}
|
3132 |
|
|
|
3133 |
|
|
/******************************************************************************/
|
3134 |
|
|
/* Function: ip2_ipl_ioctl() */
|
3135 |
|
|
/* Parameters: Pointer to device inode */
|
3136 |
|
|
/* Pointer to file structure */
|
3137 |
|
|
/* Command */
|
3138 |
|
|
/* Argument */
|
3139 |
|
|
/* Returns: Success or failure */
|
3140 |
|
|
/* */
|
3141 |
|
|
/* Description: */
|
3142 |
|
|
/* */
|
3143 |
|
|
/* */
|
3144 |
|
|
/******************************************************************************/
|
3145 |
|
|
static int
|
3146 |
|
|
ip2_ipl_ioctl ( struct inode *pInode, struct file *pFile, UINT cmd, ULONG arg )
|
3147 |
|
|
{
|
3148 |
|
|
unsigned int iplminor = MINOR(pInode->i_rdev);
|
3149 |
|
|
int rc = 0;
|
3150 |
|
|
ULONG *pIndex = (ULONG*)arg;
|
3151 |
|
|
i2eBordStrPtr pB = i2BoardPtrTable[iplminor / 4];
|
3152 |
|
|
i2ChanStrPtr pCh;
|
3153 |
|
|
|
3154 |
|
|
#ifdef IP2DEBUG_IPL
|
3155 |
|
|
printk (KERN_DEBUG "IP2IPL: ioctl cmd %d, arg %ld\n", cmd, arg );
|
3156 |
|
|
#endif
|
3157 |
|
|
|
3158 |
|
|
switch ( iplminor ) {
|
3159 |
|
|
case 0: // IPL device
|
3160 |
|
|
rc = -EINVAL;
|
3161 |
|
|
break;
|
3162 |
|
|
case 1: // Status dump
|
3163 |
|
|
case 5:
|
3164 |
|
|
case 9:
|
3165 |
|
|
case 13:
|
3166 |
|
|
switch ( cmd ) {
|
3167 |
|
|
case 64: /* Driver - ip2stat */
|
3168 |
|
|
PUT_USER(rc, ref_count, pIndex++ );
|
3169 |
|
|
PUT_USER(rc, irq_counter, pIndex++ );
|
3170 |
|
|
PUT_USER(rc, bh_counter, pIndex++ );
|
3171 |
|
|
break;
|
3172 |
|
|
|
3173 |
|
|
case 65: /* Board - ip2stat */
|
3174 |
|
|
if ( pB ) {
|
3175 |
|
|
COPY_TO_USER(rc, (char*)arg, (char*)pB, sizeof(i2eBordStr) );
|
3176 |
|
|
PUT_USER(rc, INB(pB->i2eStatus),
|
3177 |
|
|
(ULONG*)(arg + (ULONG)(&pB->i2eStatus) - (ULONG)pB ) );
|
3178 |
|
|
} else {
|
3179 |
|
|
rc = -ENODEV;
|
3180 |
|
|
}
|
3181 |
|
|
break;
|
3182 |
|
|
|
3183 |
|
|
default:
|
3184 |
|
|
if (cmd < IP2_MAX_PORTS) {
|
3185 |
|
|
pCh = DevTable[cmd];
|
3186 |
|
|
if ( pCh )
|
3187 |
|
|
{
|
3188 |
|
|
COPY_TO_USER(rc, (char*)arg, (char*)pCh, sizeof(i2ChanStr) );
|
3189 |
|
|
} else {
|
3190 |
|
|
rc = -ENODEV;
|
3191 |
|
|
}
|
3192 |
|
|
} else {
|
3193 |
|
|
rc = -EINVAL;
|
3194 |
|
|
}
|
3195 |
|
|
}
|
3196 |
|
|
break;
|
3197 |
|
|
|
3198 |
|
|
case 2: // Ping device
|
3199 |
|
|
rc = -EINVAL;
|
3200 |
|
|
break;
|
3201 |
|
|
case 3: // Trace device
|
3202 |
|
|
if ( cmd == 1 ) {
|
3203 |
|
|
PUT_USER(rc, iiSendPendingMail, pIndex++ );
|
3204 |
|
|
PUT_USER(rc, i2InitChannels, pIndex++ );
|
3205 |
|
|
PUT_USER(rc, i2QueueNeeds, pIndex++ );
|
3206 |
|
|
PUT_USER(rc, i2QueueCommands, pIndex++ );
|
3207 |
|
|
PUT_USER(rc, i2GetStatus, pIndex++ );
|
3208 |
|
|
PUT_USER(rc, i2Input, pIndex++ );
|
3209 |
|
|
PUT_USER(rc, i2InputFlush, pIndex++ );
|
3210 |
|
|
PUT_USER(rc, i2Output, pIndex++ );
|
3211 |
|
|
PUT_USER(rc, i2FlushOutput, pIndex++ );
|
3212 |
|
|
PUT_USER(rc, i2DrainWakeup, pIndex++ );
|
3213 |
|
|
PUT_USER(rc, i2DrainOutput, pIndex++ );
|
3214 |
|
|
PUT_USER(rc, i2OutputFree, pIndex++ );
|
3215 |
|
|
PUT_USER(rc, i2StripFifo, pIndex++ );
|
3216 |
|
|
PUT_USER(rc, i2StuffFifoBypass, pIndex++ );
|
3217 |
|
|
PUT_USER(rc, i2StuffFifoFlow, pIndex++ );
|
3218 |
|
|
PUT_USER(rc, i2StuffFifoInline, pIndex++ );
|
3219 |
|
|
PUT_USER(rc, i2ServiceBoard, pIndex++ );
|
3220 |
|
|
PUT_USER(rc, serviceOutgoingFifo, pIndex++ );
|
3221 |
|
|
// PUT_USER(rc, ip2_init, pIndex++ );
|
3222 |
|
|
PUT_USER(rc, ip2_init_board, pIndex++ );
|
3223 |
|
|
PUT_USER(rc, find_eisa_board, pIndex++ );
|
3224 |
|
|
PUT_USER(rc, set_irq, pIndex++ );
|
3225 |
|
|
PUT_USER(rc, ip2_interrupt, pIndex++ );
|
3226 |
|
|
PUT_USER(rc, ip2_poll, pIndex++ );
|
3227 |
|
|
PUT_USER(rc, service_all_boards, pIndex++ );
|
3228 |
|
|
PUT_USER(rc, do_input, pIndex++ );
|
3229 |
|
|
PUT_USER(rc, do_status, pIndex++ );
|
3230 |
|
|
#ifndef IP2DEBUG_OPEN
|
3231 |
|
|
PUT_USER(rc, 0, pIndex++ );
|
3232 |
|
|
#else
|
3233 |
|
|
PUT_USER(rc, open_sanity_check, pIndex++ );
|
3234 |
|
|
#endif
|
3235 |
|
|
PUT_USER(rc, ip2_open, pIndex++ );
|
3236 |
|
|
PUT_USER(rc, ip2_close, pIndex++ );
|
3237 |
|
|
PUT_USER(rc, ip2_hangup, pIndex++ );
|
3238 |
|
|
PUT_USER(rc, ip2_write, pIndex++ );
|
3239 |
|
|
PUT_USER(rc, ip2_putchar, pIndex++ );
|
3240 |
|
|
PUT_USER(rc, ip2_flush_chars, pIndex++ );
|
3241 |
|
|
PUT_USER(rc, ip2_write_room, pIndex++ );
|
3242 |
|
|
PUT_USER(rc, ip2_chars_in_buf, pIndex++ );
|
3243 |
|
|
PUT_USER(rc, ip2_flush_buffer, pIndex++ );
|
3244 |
|
|
|
3245 |
|
|
//PUT_USER(rc, ip2_wait_until_sent, pIndex++ );
|
3246 |
|
|
PUT_USER(rc, 0, pIndex++ );
|
3247 |
|
|
|
3248 |
|
|
PUT_USER(rc, ip2_throttle, pIndex++ );
|
3249 |
|
|
PUT_USER(rc, ip2_unthrottle, pIndex++ );
|
3250 |
|
|
PUT_USER(rc, ip2_ioctl, pIndex++ );
|
3251 |
|
|
PUT_USER(rc, set_modem_info, pIndex++ );
|
3252 |
|
|
PUT_USER(rc, get_serial_info, pIndex++ );
|
3253 |
|
|
PUT_USER(rc, set_serial_info, pIndex++ );
|
3254 |
|
|
PUT_USER(rc, ip2_set_termios, pIndex++ );
|
3255 |
|
|
PUT_USER(rc, ip2_set_line_discipline, pIndex++ );
|
3256 |
|
|
PUT_USER(rc, set_params, pIndex++ );
|
3257 |
|
|
} else {
|
3258 |
|
|
rc = -EINVAL;
|
3259 |
|
|
}
|
3260 |
|
|
|
3261 |
|
|
break;
|
3262 |
|
|
|
3263 |
|
|
default:
|
3264 |
|
|
rc = -ENODEV;
|
3265 |
|
|
break;
|
3266 |
|
|
}
|
3267 |
|
|
return rc;
|
3268 |
|
|
}
|
3269 |
|
|
|
3270 |
|
|
/******************************************************************************/
|
3271 |
|
|
/* Function: ip2_ipl_open() */
|
3272 |
|
|
/* Parameters: Pointer to device inode */
|
3273 |
|
|
/* Pointer to file structure */
|
3274 |
|
|
/* Returns: Success or failure */
|
3275 |
|
|
/* */
|
3276 |
|
|
/* Description: */
|
3277 |
|
|
/* */
|
3278 |
|
|
/* */
|
3279 |
|
|
/******************************************************************************/
|
3280 |
|
|
static int
|
3281 |
|
|
ip2_ipl_open( struct inode *pInode, struct file *pFile )
|
3282 |
|
|
{
|
3283 |
|
|
unsigned int iplminor = MINOR(pInode->i_rdev);
|
3284 |
|
|
i2eBordStrPtr pB;
|
3285 |
|
|
i2ChanStrPtr pCh;
|
3286 |
|
|
|
3287 |
|
|
#ifdef IP2DEBUG_IPL
|
3288 |
|
|
printk (KERN_DEBUG "IP2IPL: open\n" );
|
3289 |
|
|
#endif
|
3290 |
|
|
|
3291 |
|
|
switch(iplminor) {
|
3292 |
|
|
// These are the IPL devices
|
3293 |
|
|
case 0:
|
3294 |
|
|
case 4:
|
3295 |
|
|
case 8:
|
3296 |
|
|
case 12:
|
3297 |
|
|
break;
|
3298 |
|
|
|
3299 |
|
|
// These are the status devices
|
3300 |
|
|
case 1:
|
3301 |
|
|
case 5:
|
3302 |
|
|
case 9:
|
3303 |
|
|
case 13:
|
3304 |
|
|
break;
|
3305 |
|
|
|
3306 |
|
|
// These are the debug devices
|
3307 |
|
|
case 2:
|
3308 |
|
|
case 6:
|
3309 |
|
|
case 10:
|
3310 |
|
|
case 14:
|
3311 |
|
|
pB = i2BoardPtrTable[iplminor / 4];
|
3312 |
|
|
pCh = (i2ChanStrPtr) pB->i2eChannelPtr;
|
3313 |
|
|
break;
|
3314 |
|
|
|
3315 |
|
|
// This is the trace device
|
3316 |
|
|
case 3:
|
3317 |
|
|
break;
|
3318 |
|
|
}
|
3319 |
|
|
return 0;
|
3320 |
|
|
}
|
3321 |
|
|
/******************************************************************************/
|
3322 |
|
|
/* Function: ip2_read_procmem */
|
3323 |
|
|
/* Parameters: */
|
3324 |
|
|
/* */
|
3325 |
|
|
/* Returns: Length of output */
|
3326 |
|
|
/* */
|
3327 |
|
|
/* Description: */
|
3328 |
|
|
/* Supplies some driver operating parameters */
|
3329 |
|
|
/* Not real useful unless your debugging the fifo */
|
3330 |
|
|
/* */
|
3331 |
|
|
/******************************************************************************/
|
3332 |
|
|
|
3333 |
|
|
#define LIMIT (PAGE_SIZE - 120)
|
3334 |
|
|
|
3335 |
|
|
static int
|
3336 |
|
|
ip2_read_procmem(char *buf, char **start, off_t offset, int len)
|
3337 |
|
|
{
|
3338 |
|
|
i2eBordStrPtr pB;
|
3339 |
|
|
i2ChanStrPtr pCh;
|
3340 |
|
|
PTTY tty;
|
3341 |
|
|
int i;
|
3342 |
|
|
|
3343 |
|
|
len = 0;
|
3344 |
|
|
|
3345 |
|
|
#define FMTLINE "%3d: 0x%08x 0x%08x 0%011o 0%011o\n"
|
3346 |
|
|
#define FMTLIN2 " 0x%04x 0x%04x tx flow 0x%x\n"
|
3347 |
|
|
#define FMTLIN3 " 0x%04x 0x%04x rc flow\n"
|
3348 |
|
|
|
3349 |
|
|
len += sprintf(buf+len,"\n");
|
3350 |
|
|
|
3351 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
3352 |
|
|
pB = i2BoardPtrTable[i];
|
3353 |
|
|
if ( pB ) {
|
3354 |
|
|
len += sprintf(buf+len,"board %d:\n",i);
|
3355 |
|
|
len += sprintf(buf+len,"\tFifo rem: %d mty: %x outM %x\n",
|
3356 |
|
|
pB->i2eFifoRemains,pB->i2eWaitingForEmptyFifo,pB->i2eOutMailWaiting);
|
3357 |
|
|
}
|
3358 |
|
|
}
|
3359 |
|
|
|
3360 |
|
|
len += sprintf(buf+len,"#: tty flags, port flags, cflags, iflags\n");
|
3361 |
|
|
for (i=0; i < IP2_MAX_PORTS; i++) {
|
3362 |
|
|
if (len > LIMIT)
|
3363 |
|
|
break;
|
3364 |
|
|
pCh = DevTable[i];
|
3365 |
|
|
if (pCh) {
|
3366 |
|
|
tty = pCh->pTTY;
|
3367 |
|
|
if (tty && tty->count) {
|
3368 |
|
|
len += sprintf(buf+len,FMTLINE,i,(int)tty->flags,pCh->flags,
|
3369 |
|
|
tty->termios->c_cflag,tty->termios->c_iflag);
|
3370 |
|
|
|
3371 |
|
|
len += sprintf(buf+len,FMTLIN2,
|
3372 |
|
|
pCh->outfl.asof,pCh->outfl.room,pCh->channelNeeds);
|
3373 |
|
|
len += sprintf(buf+len,FMTLIN3,pCh->infl.asof,pCh->infl.room);
|
3374 |
|
|
}
|
3375 |
|
|
}
|
3376 |
|
|
}
|
3377 |
|
|
return len;
|
3378 |
|
|
}
|
3379 |
|
|
|
3380 |
|
|
/*
|
3381 |
|
|
* This is the handler for /proc/tty/driver/ip2
|
3382 |
|
|
*
|
3383 |
|
|
* This stretch of code has been largely plagerized from at least three
|
3384 |
|
|
* different sources including ip2mkdev.c and a couple of other drivers.
|
3385 |
|
|
* The bugs are all mine. :-) =mhw=
|
3386 |
|
|
*/
|
3387 |
|
|
int ip2_read_proc(char *page, char **start, off_t off,
|
3388 |
|
|
int count, int *eof, void *data)
|
3389 |
|
|
{
|
3390 |
|
|
int i, j, box;
|
3391 |
|
|
int len = 0;
|
3392 |
|
|
int boxes = 0;
|
3393 |
|
|
int ports = 0;
|
3394 |
|
|
int tports = 0;
|
3395 |
|
|
off_t begin = 0;
|
3396 |
|
|
i2eBordStrPtr pB;
|
3397 |
|
|
|
3398 |
|
|
len += sprintf(page, "ip2info: 1.0 driver: %s\n", pcVersion );
|
3399 |
|
|
len += sprintf(page+len, "Driver: SMajor=%d CMajor=%d IMajor=%d MaxBoards=%d MaxBoxes=%d MaxPorts=%d\n",
|
3400 |
|
|
IP2_TTY_MAJOR, IP2_CALLOUT_MAJOR, IP2_IPL_MAJOR,
|
3401 |
|
|
IP2_MAX_BOARDS, ABS_MAX_BOXES, ABS_BIGGEST_BOX);
|
3402 |
|
|
|
3403 |
|
|
for( i = 0; i < IP2_MAX_BOARDS; ++i ) {
|
3404 |
|
|
/* This need to be reset for a board by board count... */
|
3405 |
|
|
boxes = 0;
|
3406 |
|
|
pB = i2BoardPtrTable[i];
|
3407 |
|
|
if( pB ) {
|
3408 |
|
|
switch( pB->i2ePom.e.porID & ~POR_ID_RESERVED )
|
3409 |
|
|
{
|
3410 |
|
|
case POR_ID_FIIEX:
|
3411 |
|
|
len += sprintf( page+len, "Board %d: EX ports=", i );
|
3412 |
|
|
for( box = 0; box < ABS_MAX_BOXES; ++box )
|
3413 |
|
|
{
|
3414 |
|
|
ports = 0;
|
3415 |
|
|
|
3416 |
|
|
if( pB->i2eChannelMap[box] != 0 ) ++boxes;
|
3417 |
|
|
for( j = 0; j < ABS_BIGGEST_BOX; ++j )
|
3418 |
|
|
{
|
3419 |
|
|
if( pB->i2eChannelMap[box] & 1<< j ) {
|
3420 |
|
|
++ports;
|
3421 |
|
|
}
|
3422 |
|
|
}
|
3423 |
|
|
len += sprintf( page+len, "%d,", ports );
|
3424 |
|
|
tports += ports;
|
3425 |
|
|
}
|
3426 |
|
|
|
3427 |
|
|
--len; /* Backup over that last comma */
|
3428 |
|
|
|
3429 |
|
|
len += sprintf( page+len, " boxes=%d width=%d", boxes, pB->i2eDataWidth16 ? 16 : 8 );
|
3430 |
|
|
break;
|
3431 |
|
|
|
3432 |
|
|
case POR_ID_II_4:
|
3433 |
|
|
len += sprintf(page+len, "Board %d: ISA-4 ports=4 boxes=1", i );
|
3434 |
|
|
tports = ports = 4;
|
3435 |
|
|
break;
|
3436 |
|
|
|
3437 |
|
|
case POR_ID_II_8:
|
3438 |
|
|
len += sprintf(page+len, "Board %d: ISA-8-std ports=8 boxes=1", i );
|
3439 |
|
|
tports = ports = 8;
|
3440 |
|
|
break;
|
3441 |
|
|
|
3442 |
|
|
case POR_ID_II_8R:
|
3443 |
|
|
len += sprintf(page+len, "Board %d: ISA-8-RJ11 ports=8 boxes=1", i );
|
3444 |
|
|
tports = ports = 8;
|
3445 |
|
|
break;
|
3446 |
|
|
|
3447 |
|
|
default:
|
3448 |
|
|
len += sprintf(page+len, "Board %d: unknown", i );
|
3449 |
|
|
/* Don't try and probe for minor numbers */
|
3450 |
|
|
tports = ports = 0;
|
3451 |
|
|
}
|
3452 |
|
|
|
3453 |
|
|
} else {
|
3454 |
|
|
/* Don't try and probe for minor numbers */
|
3455 |
|
|
len += sprintf(page+len, "Board %d: vacant", i );
|
3456 |
|
|
tports = ports = 0;
|
3457 |
|
|
}
|
3458 |
|
|
|
3459 |
|
|
if( tports ) {
|
3460 |
|
|
len += sprintf(page+len, " minors=" );
|
3461 |
|
|
|
3462 |
|
|
for ( box = 0; box < ABS_MAX_BOXES; ++box )
|
3463 |
|
|
{
|
3464 |
|
|
for ( j = 0; j < ABS_BIGGEST_BOX; ++j )
|
3465 |
|
|
{
|
3466 |
|
|
if ( pB->i2eChannelMap[box] & (1 << j) )
|
3467 |
|
|
{
|
3468 |
|
|
len += sprintf (page+len,"%d,",
|
3469 |
|
|
j + ABS_BIGGEST_BOX *
|
3470 |
|
|
(box+i*ABS_MAX_BOXES));
|
3471 |
|
|
}
|
3472 |
|
|
}
|
3473 |
|
|
}
|
3474 |
|
|
|
3475 |
|
|
page[ len - 1 ] = '\n'; /* Overwrite that last comma */
|
3476 |
|
|
} else {
|
3477 |
|
|
len += sprintf (page+len,"\n" );
|
3478 |
|
|
}
|
3479 |
|
|
|
3480 |
|
|
if (len+begin > off+count)
|
3481 |
|
|
break;
|
3482 |
|
|
if (len+begin < off) {
|
3483 |
|
|
begin += len;
|
3484 |
|
|
len = 0;
|
3485 |
|
|
}
|
3486 |
|
|
}
|
3487 |
|
|
|
3488 |
|
|
if (i >= IP2_MAX_BOARDS)
|
3489 |
|
|
*eof = 1;
|
3490 |
|
|
if (off >= len+begin)
|
3491 |
|
|
return 0;
|
3492 |
|
|
|
3493 |
|
|
*start = page + (off-begin);
|
3494 |
|
|
return ((count < begin+len-off) ? count : begin+len-off);
|
3495 |
|
|
}
|
3496 |
|
|
|
3497 |
|
|
/******************************************************************************/
|
3498 |
|
|
/* Function: ip2trace() */
|
3499 |
|
|
/* Parameters: Value to add to trace buffer */
|
3500 |
|
|
/* Returns: Nothing */
|
3501 |
|
|
/* */
|
3502 |
|
|
/* Description: */
|
3503 |
|
|
/* */
|
3504 |
|
|
/* */
|
3505 |
|
|
/******************************************************************************/
|
3506 |
|
|
#ifdef IP2DEBUG_TRACE
|
3507 |
|
|
void
|
3508 |
|
|
ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned long codes, ...)
|
3509 |
|
|
{
|
3510 |
|
|
long flags;
|
3511 |
|
|
unsigned long *pCode = &codes;
|
3512 |
|
|
union ip2breadcrumb bc;
|
3513 |
|
|
i2ChanStrPtr pCh;
|
3514 |
|
|
|
3515 |
|
|
|
3516 |
|
|
tracebuf[tracestuff++] = jiffies;
|
3517 |
|
|
if ( tracestuff == TRACEMAX ) {
|
3518 |
|
|
tracestuff = 0;
|
3519 |
|
|
}
|
3520 |
|
|
if ( tracestuff == tracestrip ) {
|
3521 |
|
|
if ( ++tracestrip == TRACEMAX ) {
|
3522 |
|
|
tracestrip = 0;
|
3523 |
|
|
}
|
3524 |
|
|
++tracewrap;
|
3525 |
|
|
}
|
3526 |
|
|
|
3527 |
|
|
bc.hdr.port = 0xff & pn;
|
3528 |
|
|
bc.hdr.cat = cat;
|
3529 |
|
|
bc.hdr.codes = (unsigned char)( codes & 0xff );
|
3530 |
|
|
bc.hdr.label = label;
|
3531 |
|
|
tracebuf[tracestuff++] = bc.value;
|
3532 |
|
|
|
3533 |
|
|
for (;;) {
|
3534 |
|
|
if ( tracestuff == TRACEMAX ) {
|
3535 |
|
|
tracestuff = 0;
|
3536 |
|
|
}
|
3537 |
|
|
if ( tracestuff == tracestrip ) {
|
3538 |
|
|
if ( ++tracestrip == TRACEMAX ) {
|
3539 |
|
|
tracestrip = 0;
|
3540 |
|
|
}
|
3541 |
|
|
++tracewrap;
|
3542 |
|
|
}
|
3543 |
|
|
|
3544 |
|
|
if ( !codes-- )
|
3545 |
|
|
break;
|
3546 |
|
|
|
3547 |
|
|
tracebuf[tracestuff++] = *++pCode;
|
3548 |
|
|
}
|
3549 |
|
|
}
|
3550 |
|
|
#endif
|
3551 |
|
|
|
3552 |
|
|
|
3553 |
|
|
MODULE_LICENSE("GPL");
|