blob: 9ef8ab301768424ac6d20f1ae3e03c438d61ded3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version
5 * 2 of the License, or (at your option) any later version.
6 *
7 * Original driver code supplied by Multi-Tech
8 *
9 * Changes
10 * 1/9/98 alan@redhat.com Merge to 2.0.x kernel tree
11 * Obtain and use official major/minors
12 * Loader switched to a misc device
13 * (fixed range check bug as a side effect)
14 * Printk clean up
15 * 9/12/98 alan@redhat.com Rough port to 2.1.x
16 *
17 * 10/6/99 sameer Merged the ISA and PCI drivers to
18 * a new unified driver.
19 *
20 * 3/9/99 sameer Added support for ISI4616 cards.
21 *
22 * 16/9/99 sameer We do not force RTS low anymore.
Jiri Slabyd8d16e42006-01-09 20:54:21 -080023 * This is to prevent the firmware
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 * from getting confused.
25 *
26 * 26/10/99 sameer Cosmetic changes:The driver now
27 * dumps the Port Count information
28 * along with I/O address and IRQ.
29 *
30 * 13/12/99 sameer Fixed the problem with IRQ sharing.
31 *
32 * 10/5/00 sameer Fixed isicom_shutdown_board()
33 * to not lower DTR on all the ports
Jiri Slabyd8d16e42006-01-09 20:54:21 -080034 * when the last port on the card is
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 * closed.
36 *
37 * 10/5/00 sameer Signal mask setup command added
Jiri Slabyd8d16e42006-01-09 20:54:21 -080038 * to isicom_setup_port and
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 * isicom_shutdown_port.
40 *
41 * 24/5/00 sameer The driver is now SMP aware.
Jiri Slabyd8d16e42006-01-09 20:54:21 -080042 *
43 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 * 27/11/00 Vinayak P Risbud Fixed the Driver Crash Problem
Jiri Slabyd8d16e42006-01-09 20:54:21 -080045 *
46 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 * 03/01/01 anil .s Added support for resetting the
48 * internal modems on ISI cards.
49 *
50 * 08/02/01 anil .s Upgraded the driver for kernel
51 * 2.4.x
52 *
Jiri Slabyd8d16e42006-01-09 20:54:21 -080053 * 11/04/01 Kevin Fixed firmware load problem with
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 * ISIHP-4X card
Jiri Slabyd8d16e42006-01-09 20:54:21 -080055 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 * 30/04/01 anil .s Fixed the remote login through
57 * ISI port problem. Now the link
58 * does not go down before password
59 * prompt.
60 *
61 * 03/05/01 anil .s Fixed the problem with IRQ sharing
62 * among ISI-PCI cards.
63 *
64 * 03/05/01 anil .s Added support to display the version
Jiri Slabyd8d16e42006-01-09 20:54:21 -080065 * info during insmod as well as module
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 * listing by lsmod.
Jiri Slabyd8d16e42006-01-09 20:54:21 -080067 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 * 10/05/01 anil .s Done the modifications to the source
69 * file and Install script so that the
70 * same installation can be used for
71 * 2.2.x and 2.4.x kernel.
72 *
73 * 06/06/01 anil .s Now we drop both dtr and rts during
74 * shutdown_port as well as raise them
75 * during isicom_config_port.
Jiri Slabyd8d16e42006-01-09 20:54:21 -080076 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 * 09/06/01 acme@conectiva.com.br use capable, not suser, do
78 * restore_flags on failure in
79 * isicom_send_break, verify put_user
80 * result
81 *
Jiri Slabyd8d16e42006-01-09 20:54:21 -080082 * 11/02/03 ranjeeth Added support for 230 Kbps and 460 Kbps
83 * Baud index extended to 21
84 *
85 * 20/03/03 ranjeeth Made to work for Linux Advanced server.
86 * Taken care of license warning.
87 *
88 * 10/12/03 Ravindra Made to work for Fedora Core 1 of
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 * Red Hat Distribution
90 *
91 * 06/01/05 Alan Cox Merged the ISI and base kernel strands
92 * into a single 2.6 driver
93 *
94 * ***********************************************************
95 *
Jiri Slabyd8d16e42006-01-09 20:54:21 -080096 * To use this driver you also need the support package. You
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 * can find this in RPM format on
98 * ftp://ftp.linux.org.uk/pub/linux/alan
Jiri Slabyd8d16e42006-01-09 20:54:21 -080099 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 * You can find the original tools for this direct from Multitech
101 * ftp://ftp.multitech.com/ISI-Cards/
102 *
103 * Having installed the cards the module options (/etc/modprobe.conf)
104 *
105 * options isicom io=card1,card2,card3,card4 irq=card1,card2,card3,card4
106 *
107 * Omit those entries for boards you don't have installed.
108 *
109 * TODO
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 * Merge testing
111 * 64-bit verification
112 */
113
114#include <linux/module.h>
115#include <linux/kernel.h>
116#include <linux/tty.h>
Alan Cox33f0f882006-01-09 20:54:13 -0800117#include <linux/tty_flip.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118#include <linux/termios.h>
119#include <linux/fs.h>
120#include <linux/sched.h>
121#include <linux/serial.h>
122#include <linux/mm.h>
123#include <linux/miscdevice.h>
124#include <linux/interrupt.h>
125#include <linux/timer.h>
126#include <linux/delay.h>
127#include <linux/ioport.h>
128
129#include <asm/uaccess.h>
130#include <asm/io.h>
131#include <asm/system.h>
132
133#include <linux/pci.h>
134
135#include <linux/isicom.h>
136
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800137#define InterruptTheCard(base) outw(0, (base) + 0xc)
138#define ClearInterrupt(base) inw((base) + 0x0a)
139
140#ifdef DEBUG
141#define pr_dbg(str...) printk(KERN_DEBUG "ISICOM: " str)
142#define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))
143#else
144#define pr_dbg(str...) do { } while (0)
145#define isicom_paranoia_check(a, b, c) 0
146#endif
147
Jiri Slaby9ac09482006-01-09 20:54:24 -0800148static int isicom_probe(struct pci_dev *, const struct pci_device_id *);
149static void __devexit isicom_remove(struct pci_dev *);
150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151static struct pci_device_id isicom_pci_tbl[] = {
Jiri Slaby9ac09482006-01-09 20:54:24 -0800152 { PCI_DEVICE(VENDOR_ID, 0x2028) },
153 { PCI_DEVICE(VENDOR_ID, 0x2051) },
154 { PCI_DEVICE(VENDOR_ID, 0x2052) },
155 { PCI_DEVICE(VENDOR_ID, 0x2053) },
156 { PCI_DEVICE(VENDOR_ID, 0x2054) },
157 { PCI_DEVICE(VENDOR_ID, 0x2055) },
158 { PCI_DEVICE(VENDOR_ID, 0x2056) },
159 { PCI_DEVICE(VENDOR_ID, 0x2057) },
160 { PCI_DEVICE(VENDOR_ID, 0x2058) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 { 0 }
162};
163MODULE_DEVICE_TABLE(pci, isicom_pci_tbl);
164
Jiri Slaby9ac09482006-01-09 20:54:24 -0800165static struct pci_driver isicom_driver = {
166 .name = "isicom",
167 .id_table = isicom_pci_tbl,
168 .probe = isicom_probe,
169 .remove = __devexit_p(isicom_remove)
170};
171
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172static int prev_card = 3; /* start servicing isi_card[0] */
173static struct tty_driver *isicom_normal;
174
175static struct timer_list tx;
176static char re_schedule = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
178static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
179
180static void isicom_tx(unsigned long _data);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800181static void isicom_start(struct tty_struct *tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183/* baud index mappings from linux defns to isi */
184
185static signed char linuxb_to_isib[] = {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800186 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187};
188
189struct isi_board {
Jiri Slaby8070e352006-01-09 20:54:22 -0800190 unsigned long base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 unsigned char irq;
192 unsigned char port_count;
193 unsigned short status;
194 unsigned short port_status; /* each bit represents a single port */
195 unsigned short shift_count;
196 struct isi_port * ports;
197 signed char count;
198 unsigned char isa;
199 spinlock_t card_lock; /* Card wide lock 11/5/00 -sameer */
200 unsigned long flags;
201};
202
203struct isi_port {
204 unsigned short magic;
205 unsigned int flags;
206 int count;
207 int blocked_open;
208 int close_delay;
Jiri Slaby8070e352006-01-09 20:54:22 -0800209 u16 channel;
210 u16 status;
211 u16 closing_wait;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 struct isi_board * card;
213 struct tty_struct * tty;
214 wait_queue_head_t close_wait;
215 wait_queue_head_t open_wait;
216 struct work_struct hangup_tq;
217 struct work_struct bh_tqueue;
218 unsigned char * xmit_buf;
219 int xmit_head;
220 int xmit_tail;
221 int xmit_cnt;
222};
223
224static struct isi_board isi_card[BOARD_COUNT];
225static struct isi_port isi_ports[PORT_COUNT];
226
227/*
228 * Locking functions for card level locking. We need to own both
229 * the kernel lock for the card and have the card in a position that
230 * it wants to talk.
231 */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233static int lock_card(struct isi_board *card)
234{
235 char retries;
Jiri Slaby8070e352006-01-09 20:54:22 -0800236 unsigned long base = card->base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
238 for (retries = 0; retries < 100; retries++) {
239 spin_lock_irqsave(&card->card_lock, card->flags);
240 if (inw(base + 0xe) & 0x1) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800241 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 } else {
243 spin_unlock_irqrestore(&card->card_lock, card->flags);
244 udelay(1000); /* 1ms */
245 }
246 }
Jiri Slaby8070e352006-01-09 20:54:22 -0800247 printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%lx)\n", card->base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return 0; /* Failed to aquire the card! */
249}
250
251static int lock_card_at_interrupt(struct isi_board *card)
252{
253 unsigned char retries;
Jiri Slaby8070e352006-01-09 20:54:22 -0800254 unsigned long base = card->base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
256 for (retries = 0; retries < 200; retries++) {
257 spin_lock_irqsave(&card->card_lock, card->flags);
258
259 if (inw(base + 0xe) & 0x1)
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800260 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 else
262 spin_unlock_irqrestore(&card->card_lock, card->flags);
263 }
264 /* Failing in interrupt is an acceptable event */
265 return 0; /* Failed to aquire the card! */
266}
267
268static void unlock_card(struct isi_board *card)
269{
270 spin_unlock_irqrestore(&card->card_lock, card->flags);
271}
272
273/*
274 * ISI Card specific ops ...
275 */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800276
277static void raise_dtr(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800279 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800280 unsigned long base = card->base;
281 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 if (!lock_card(card))
284 return;
285
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800286 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 outw(0x0504, base);
288 InterruptTheCard(base);
289 port->status |= ISI_DTR;
290 unlock_card(card);
291}
292
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800293static inline void drop_dtr(struct isi_port *port)
294{
295 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800296 unsigned long base = card->base;
297 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 if (!lock_card(card))
300 return;
301
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800302 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 outw(0x0404, base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800304 InterruptTheCard(base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 port->status &= ~ISI_DTR;
306 unlock_card(card);
307}
308
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800309static inline void raise_rts(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800311 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800312 unsigned long base = card->base;
313 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315 if (!lock_card(card))
316 return;
317
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800318 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 outw(0x0a04, base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800320 InterruptTheCard(base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 port->status |= ISI_RTS;
322 unlock_card(card);
323}
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800324static inline void drop_rts(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800326 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800327 unsigned long base = card->base;
328 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330 if (!lock_card(card))
331 return;
332
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800333 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 outw(0x0804, base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800335 InterruptTheCard(base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 port->status &= ~ISI_RTS;
337 unlock_card(card);
338}
339
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800340static inline void raise_dtr_rts(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800342 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800343 unsigned long base = card->base;
344 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
346 if (!lock_card(card))
347 return;
348
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800349 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 outw(0x0f04, base);
351 InterruptTheCard(base);
352 port->status |= (ISI_DTR | ISI_RTS);
353 unlock_card(card);
354}
355
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800356static void drop_dtr_rts(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800358 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800359 unsigned long base = card->base;
360 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361
362 if (!lock_card(card))
363 return;
364
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800365 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 outw(0x0c04, base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800367 InterruptTheCard(base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 port->status &= ~(ISI_RTS | ISI_DTR);
369 unlock_card(card);
370}
371
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800372static inline void kill_queue(struct isi_port *port, short queue)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800374 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -0800375 unsigned long base = card->base;
376 u16 channel = port->channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
378 if (!lock_card(card))
379 return;
380
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800381 outw(0x8000 | (channel << card->shift_count) | 0x02, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 outw((queue << 8) | 0x06, base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800383 InterruptTheCard(base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 unlock_card(card);
385}
386
387
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800388/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 * Firmware loader driver specific routines. This needs to mostly die
390 * and be replaced with request_firmware.
391 */
392
393static struct file_operations ISILoad_fops = {
394 .owner = THIS_MODULE,
395 .ioctl = ISILoad_ioctl,
396};
397
398static struct miscdevice isiloader_device = {
399 ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops
400};
401
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800402
Jiri Slaby8070e352006-01-09 20:54:22 -0800403static inline int WaitTillCardIsFree(unsigned long base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404{
405 unsigned long count=0;
406 while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000));
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800407 if (inw(base+0xe)&0x1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 return 0;
409 else
410 return 1;
411}
412
413static int ISILoad_ioctl(struct inode *inode, struct file *filp,
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800414 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
416 unsigned int card, i, j, signature, status, portcount = 0;
Jiri Slaby8070e352006-01-09 20:54:22 -0800417 unsigned long t, base;
418 u16 word_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 bin_frame frame;
420 void __user *argp = (void __user *)arg;
421 /* exec_record exec_rec; */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800422
423 if (get_user(card, (int __user *)argp))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 return -EFAULT;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800425
426 if (card < 0 || card >= BOARD_COUNT)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 return -ENXIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800428
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 base=isi_card[card].base;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800430
431 if (base==0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 return -ENXIO; /* disabled or not used */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800433
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 switch(cmd) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800435 case MIOCTL_RESET_CARD:
436 if (!capable(CAP_SYS_ADMIN))
437 return -EPERM;
Jiri Slaby8070e352006-01-09 20:54:22 -0800438 printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%lx ",card+1,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800439
440 inw(base+0x8);
441
442 for (t=jiffies+HZ/100;time_before(jiffies, t););
443
444 outw(0,base+0x8); /* Reset */
445
446 for (j=1;j<=3;j++) {
447 for (t=jiffies+HZ;time_before(jiffies, t););
448 printk(".");
449 }
450 signature=(inw(base+0x4)) & 0xff;
451 if (isi_card[card].isa) {
452
453 if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454#ifdef ISICOM_DEBUG
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800455 printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456#endif
Jiri Slaby8070e352006-01-09 20:54:22 -0800457 printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800458 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800460 }
461 else {
462 portcount = inw(base+0x2);
463 if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) {
464#ifdef ISICOM_DEBUG
465 printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe));
466#endif
Jiri Slaby8070e352006-01-09 20:54:22 -0800467 printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800468 return -EIO;
469 }
470 }
471 switch(signature) {
472 case 0xa5:
473 case 0xbb:
474 case 0xdd:
475 if (isi_card[card].isa)
476 isi_card[card].port_count = 8;
477 else {
478 if (portcount == 4)
479 isi_card[card].port_count = 4;
480 else
481 isi_card[card].port_count = 8;
482 }
483 isi_card[card].shift_count = 12;
484 break;
485
486 case 0xcc: isi_card[card].port_count = 16;
487 isi_card[card].shift_count = 11;
488 break;
489
Jiri Slaby8070e352006-01-09 20:54:22 -0800490 default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800491#ifdef ISICOM_DEBUG
492 printk("Sig=0x%x\n",signature);
493#endif
494 return -EIO;
495 }
496 printk("-Done\n");
497 return put_user(signature,(unsigned __user *)argp);
498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 case MIOCTL_LOAD_FIRMWARE:
500 if (!capable(CAP_SYS_ADMIN))
501 return -EPERM;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800502
503 if (copy_from_user(&frame, argp, sizeof(bin_frame)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return -EFAULT;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800505
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 if (WaitTillCardIsFree(base))
507 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800508
509 outw(0xf0,base); /* start upload sequence */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 outw(0x00,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800511 outw((frame.addr), base); /* lsb of adderess */
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 word_count=(frame.count >> 1) + frame.count % 2;
514 outw(word_count, base);
515 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800516
517 for (i=0;i<=0x2f;i++); /* a wee bit of delay */
518
519 if (WaitTillCardIsFree(base))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 if ((status=inw(base+0x4))!=0) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800523 printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 card+1, frame.addr, frame.count, status);
525 return -EIO;
526 }
527 outsw(base, (void *) frame.bin_data, word_count);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800528
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800530
531 for (i=0;i<=0x0f;i++); /* another wee bit of delay */
532
533 if (WaitTillCardIsFree(base))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if ((status=inw(base+0x4))!=0) {
537 printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status);
538 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800539 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 case MIOCTL_READ_FIRMWARE:
543 if (!capable(CAP_SYS_ADMIN))
544 return -EPERM;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800545
546 if (copy_from_user(&frame, argp, sizeof(bin_header)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 return -EFAULT;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 if (WaitTillCardIsFree(base))
550 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800551
552 outw(0xf1,base); /* start download sequence */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 outw(0x00,base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800554 outw((frame.addr), base); /* lsb of adderess */
555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 word_count=(frame.count >> 1) + frame.count % 2;
557 outw(word_count+1, base);
558 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800559
560 for (i=0;i<=0xf;i++); /* a wee bit of delay */
561
562 if (WaitTillCardIsFree(base))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800564
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 if ((status=inw(base+0x4))!=0) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800566 printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 card+1, frame.addr, frame.count, status);
568 return -EIO;
569 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 inw(base);
572 insw(base, frame.bin_data, word_count);
573 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800574
575 for (i=0;i<=0x0f;i++); /* another wee bit of delay */
576
577 if (WaitTillCardIsFree(base))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800579
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 if ((status=inw(base+0x4))!=0) {
581 printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status);
582 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800583 }
584
585 if (copy_to_user(argp, &frame, sizeof(bin_frame)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 return -EFAULT;
587 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 case MIOCTL_XFER_CTRL:
590 if (!capable(CAP_SYS_ADMIN))
591 return -EPERM;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800592 if (WaitTillCardIsFree(base))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 return -EIO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800594
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 outw(0xf2, base);
596 outw(0x800, base);
597 outw(0x0, base);
598 outw(0x0, base);
599 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800600 outw(0x0, base+0x4); /* for ISI4608 cards */
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 isi_card[card].status |= FIRMWARE_LOADED;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800603 return 0;
604
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 default:
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800606#ifdef ISICOM_DEBUG
607 printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608#endif
609 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611}
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
614/*
615 * ISICOM Driver specific routines ...
616 *
617 */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800618
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800619static inline int __isicom_paranoia_check(struct isi_port const *port,
620 char *name, const char *routine)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 if (!port) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800623 printk(KERN_WARNING "ISICOM: Warning: bad isicom magic for "
624 "dev %s in %s.\n", name, routine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return 1;
626 }
627 if (port->magic != ISICOM_MAGIC) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800628 printk(KERN_WARNING "ISICOM: Warning: NULL isicom port for "
629 "dev %s in %s.\n", name, routine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 return 1;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800631 }
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800632
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 return 0;
634}
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800635
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636/*
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800637 * Transmitter.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 *
639 * We shovel data into the card buffers on a regular basis. The card
640 * will do the rest of the work for us.
641 */
642
643static void isicom_tx(unsigned long _data)
644{
645 short count = (BOARD_COUNT-1), card, base;
646 short txcount, wrd, residue, word_count, cnt;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800647 struct isi_port *port;
648 struct tty_struct *tty;
649
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 /* find next active board */
651 card = (prev_card + 1) & 0x0003;
652 while(count-- > 0) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800653 if (isi_card[card].status & BOARD_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 break;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800655 card = (card + 1) & 0x0003;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 }
657 if (!(isi_card[card].status & BOARD_ACTIVE))
658 goto sched_again;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 prev_card = card;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800661
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 count = isi_card[card].port_count;
663 port = isi_card[card].ports;
664 base = isi_card[card].base;
665 for (;count > 0;count--, port++) {
666 if (!lock_card_at_interrupt(&isi_card[card]))
667 continue;
668 /* port not active or tx disabled to force flow control */
669 if (!(port->flags & ASYNC_INITIALIZED) ||
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800670 !(port->status & ISI_TXOK))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 unlock_card(&isi_card[card]);
672 continue;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 tty = port->tty;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800675
676
677 if (tty == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 unlock_card(&isi_card[card]);
679 continue;
680 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 txcount = min_t(short, TX_SIZE, port->xmit_cnt);
683 if (txcount <= 0 || tty->stopped || tty->hw_stopped) {
684 unlock_card(&isi_card[card]);
685 continue;
686 }
687 if (!(inw(base + 0x02) & (1 << port->channel))) {
688 unlock_card(&isi_card[card]);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800689 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 }
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800691 pr_dbg("txing %d bytes, port%d.\n", txcount,
692 port->channel + 1);
693 outw((port->channel << isi_card[card].shift_count) | txcount,
694 base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 residue = NO;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800696 wrd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 while (1) {
698 cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - port->xmit_tail));
699 if (residue == YES) {
700 residue = NO;
701 if (cnt > 0) {
702 wrd |= (port->xmit_buf[port->xmit_tail] << 8);
703 port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
704 port->xmit_cnt--;
705 txcount--;
706 cnt--;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800707 outw(wrd, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
709 else {
710 outw(wrd, base);
711 break;
712 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800713 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 if (cnt <= 0) break;
715 word_count = cnt >> 1;
716 outsw(base, port->xmit_buf+port->xmit_tail, word_count);
717 port->xmit_tail = (port->xmit_tail + (word_count << 1)) &
718 (SERIAL_XMIT_SIZE - 1);
719 txcount -= (word_count << 1);
720 port->xmit_cnt -= (word_count << 1);
721 if (cnt & 0x0001) {
722 residue = YES;
723 wrd = port->xmit_buf[port->xmit_tail];
724 port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
725 port->xmit_cnt--;
726 txcount--;
727 }
728 }
729
730 InterruptTheCard(base);
731 if (port->xmit_cnt <= 0)
732 port->status &= ~ISI_TXOK;
733 if (port->xmit_cnt <= WAKEUP_CHARS)
734 schedule_work(&port->bh_tqueue);
735 unlock_card(&isi_card[card]);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800736 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800738 /* schedule another tx for hopefully in about 10ms */
739sched_again:
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800740 if (!re_schedule) {
741 re_schedule = 2;
742 return;
743 }
744
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 init_timer(&tx);
746 tx.expires = jiffies + HZ/100;
747 tx.data = 0;
748 tx.function = isicom_tx;
749 add_timer(&tx);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800750
751 return;
752}
753
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754/* Interrupt handlers */
755
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800756
757static void isicom_bottomhalf(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800759 struct isi_port *port = (struct isi_port *) data;
760 struct tty_struct *tty = port->tty;
761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 if (!tty)
763 return;
764
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800765 tty_wakeup(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 wake_up_interruptible(&tty->write_wait);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800767}
768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769/*
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800770 * Main interrupt handler routine
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800772
Jiri Slaby8070e352006-01-09 20:54:22 -0800773static irqreturn_t isicom_interrupt(int irq, void *dev_id, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774{
Jiri Slaby8070e352006-01-09 20:54:22 -0800775 struct isi_board *card = dev_id;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800776 struct isi_port *port;
777 struct tty_struct *tty;
Jiri Slaby8070e352006-01-09 20:54:22 -0800778 unsigned long base;
779 u16 header, word_count, count, channel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 short byte_count;
Alan Cox33f0f882006-01-09 20:54:13 -0800781 unsigned char *rp;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800782
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 if (!card || !(card->status & FIRMWARE_LOADED))
784 return IRQ_NONE;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800785
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 base = card->base;
787 spin_lock(&card->card_lock);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800788
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 if (card->isa == NO) {
790 /*
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800791 * disable any interrupts from the PCI card and lower the
792 * interrupt line
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 */
794 outw(0x8000, base+0x04);
795 ClearInterrupt(base);
796 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 inw(base); /* get the dummy word out */
799 header = inw(base);
800 channel = (header & 0x7800) >> card->shift_count;
801 byte_count = header & 0xff;
802
803 if (channel + 1 > card->port_count) {
Jiri Slaby8070e352006-01-09 20:54:22 -0800804 printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): %d(channel) > port_count.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 base, channel+1);
806 if (card->isa)
807 ClearInterrupt(base);
808 else
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800809 outw(0x0000, base+0x04); /* enable interrupts */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 spin_unlock(&card->card_lock);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800811 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 }
813 port = card->ports + channel;
814 if (!(port->flags & ASYNC_INITIALIZED)) {
815 if (card->isa)
816 ClearInterrupt(base);
817 else
818 outw(0x0000, base+0x04); /* enable interrupts */
819 return IRQ_HANDLED;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800820 }
821
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 tty = port->tty;
823 if (tty == NULL) {
824 word_count = byte_count >> 1;
825 while(byte_count > 1) {
826 inw(base);
827 byte_count -= 2;
828 }
829 if (byte_count & 0x01)
830 inw(base);
831 if (card->isa == YES)
832 ClearInterrupt(base);
833 else
834 outw(0x0000, base+0x04); /* enable interrupts */
835 spin_unlock(&card->card_lock);
836 return IRQ_HANDLED;
837 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800838
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 if (header & 0x8000) { /* Status Packet */
840 header = inw(base);
841 switch(header & 0xff) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800842 case 0: /* Change in EIA signals */
843
844 if (port->flags & ASYNC_CHECK_CD) {
845 if (port->status & ISI_DCD) {
846 if (!(header & ISI_DCD)) {
847 /* Carrier has been lost */
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800848 pr_dbg("interrupt: DCD->low.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 port->status &= ~ISI_DCD;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800850 schedule_work(&port->hangup_tq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 }
852 }
853 else {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800854 if (header & ISI_DCD) {
855 /* Carrier has been detected */
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800856 pr_dbg("interrupt: DCD->high.\n");
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800857 port->status |= ISI_DCD;
858 wake_up_interruptible(&port->open_wait);
859 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800861 }
862 else {
863 if (header & ISI_DCD)
864 port->status |= ISI_DCD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 else
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800866 port->status &= ~ISI_DCD;
867 }
868
869 if (port->flags & ASYNC_CTS_FLOW) {
870 if (port->tty->hw_stopped) {
871 if (header & ISI_CTS) {
872 port->tty->hw_stopped = 0;
873 /* start tx ing */
874 port->status |= (ISI_TXOK | ISI_CTS);
875 schedule_work(&port->bh_tqueue);
876 }
877 }
878 else {
879 if (!(header & ISI_CTS)) {
880 port->tty->hw_stopped = 1;
881 /* stop tx ing */
882 port->status &= ~(ISI_TXOK | ISI_CTS);
883 }
884 }
885 }
886 else {
887 if (header & ISI_CTS)
888 port->status |= ISI_CTS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 else
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800890 port->status &= ~ISI_CTS;
891 }
892
893 if (header & ISI_DSR)
894 port->status |= ISI_DSR;
895 else
896 port->status &= ~ISI_DSR;
897
898 if (header & ISI_RI)
899 port->status |= ISI_RI;
900 else
901 port->status &= ~ISI_RI;
902
903 break;
904
905 case 1: /* Received Break !!! */
906 tty_insert_flip_char(tty, 0, TTY_BREAK);
907 if (port->flags & ASYNC_SAK)
908 do_SAK(tty);
909 tty_flip_buffer_push(tty);
910 break;
911
912 case 2: /* Statistics */
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800913 pr_dbg("isicom_interrupt: stats!!!.\n");
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800914 break;
915
916 default:
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800917 pr_dbg("Intr: Unknown code in status packet.\n");
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800918 break;
919 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 }
921 else { /* Data Packet */
Alan Cox33f0f882006-01-09 20:54:13 -0800922
923 count = tty_prepare_flip_string(tty, &rp, byte_count & ~1);
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800924 pr_dbg("Intr: Can rx %d of %d bytes.\n", count, byte_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 word_count = count >> 1;
Alan Cox33f0f882006-01-09 20:54:13 -0800926 insw(base, rp, word_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 byte_count -= (word_count << 1);
928 if (count & 0x0001) {
Alan Cox33f0f882006-01-09 20:54:13 -0800929 tty_insert_flip_char(tty, inw(base) & 0xff, TTY_NORMAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 byte_count -= 2;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 if (byte_count > 0) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -0800933 pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping "
934 "bytes...\n", base, channel + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 while(byte_count > 0) { /* drain out unread xtra data */
936 inw(base);
937 byte_count -= 2;
938 }
939 }
Alan Cox33f0f882006-01-09 20:54:13 -0800940 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 }
942 if (card->isa == YES)
943 ClearInterrupt(base);
944 else
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800945 outw(0x0000, base+0x04); /* enable interrupts */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 return IRQ_HANDLED;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800947}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800949static void isicom_config_port(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950{
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800951 struct isi_board *card = port->card;
952 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 unsigned long baud;
Jiri Slaby8070e352006-01-09 20:54:22 -0800954 unsigned long base = card->base;
955 u16 channel_setup, channel = port->channel,
956 shift_count = card->shift_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 unsigned char flow_ctrl;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800958
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959 if (!(tty = port->tty) || !tty->termios)
960 return;
961 baud = C_BAUD(tty);
962 if (baud & CBAUDEX) {
963 baud &= ~CBAUDEX;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800964
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 /* if CBAUDEX bit is on and the baud is set to either 50 or 75
966 * then the card is programmed for 57.6Kbps or 115Kbps
967 * respectively.
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800968 */
969
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 if (baud < 1 || baud > 2)
971 port->tty->termios->c_cflag &= ~CBAUDEX;
972 else
973 baud += 15;
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800974 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 if (baud == 15) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800976
977 /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 * by the set_serial_info ioctl ... this is done by
979 * the 'setserial' utility.
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800980 */
981
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800983 baud++; /* 57.6 Kbps */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800985 baud +=2; /* 115 Kbps */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 }
987 if (linuxb_to_isib[baud] == -1) {
988 /* hang up */
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800989 drop_dtr(port);
990 return;
991 }
992 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 raise_dtr(port);
Jiri Slabyd8d16e42006-01-09 20:54:21 -0800994
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 if (lock_card(card)) {
996 outw(0x8000 | (channel << shift_count) |0x03, base);
997 outw(linuxb_to_isib[baud] << 8 | 0x03, base);
998 channel_setup = 0;
999 switch(C_CSIZE(tty)) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001000 case CS5:
1001 channel_setup |= ISICOM_CS5;
1002 break;
1003 case CS6:
1004 channel_setup |= ISICOM_CS6;
1005 break;
1006 case CS7:
1007 channel_setup |= ISICOM_CS7;
1008 break;
1009 case CS8:
1010 channel_setup |= ISICOM_CS8;
1011 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001013
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 if (C_CSTOPB(tty))
1015 channel_setup |= ISICOM_2SB;
1016 if (C_PARENB(tty)) {
1017 channel_setup |= ISICOM_EVPAR;
1018 if (C_PARODD(tty))
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001019 channel_setup |= ISICOM_ODPAR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001021 outw(channel_setup, base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 InterruptTheCard(base);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001023 unlock_card(card);
1024 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 if (C_CLOCAL(tty))
1026 port->flags &= ~ASYNC_CHECK_CD;
1027 else
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001028 port->flags |= ASYNC_CHECK_CD;
1029
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 /* flow control settings ...*/
1031 flow_ctrl = 0;
1032 port->flags &= ~ASYNC_CTS_FLOW;
1033 if (C_CRTSCTS(tty)) {
1034 port->flags |= ASYNC_CTS_FLOW;
1035 flow_ctrl |= ISICOM_CTSRTS;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001036 }
1037 if (I_IXON(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 flow_ctrl |= ISICOM_RESPOND_XONXOFF;
1039 if (I_IXOFF(tty))
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001040 flow_ctrl |= ISICOM_INITIATE_XONXOFF;
1041
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 if (lock_card(card)) {
1043 outw(0x8000 | (channel << shift_count) |0x04, base);
1044 outw(flow_ctrl << 8 | 0x05, base);
1045 outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
1046 InterruptTheCard(base);
1047 unlock_card(card);
1048 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001049
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 /* rx enabled -> enable port for rx on the card */
1051 if (C_CREAD(tty)) {
1052 card->port_status |= (1 << channel);
1053 outw(card->port_status, base + 0x02);
1054 }
1055}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001057/* open et all */
1058
1059static inline void isicom_setup_board(struct isi_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060{
1061 int channel;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001062 struct isi_port *port;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001064
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 spin_lock_irqsave(&bp->card_lock, flags);
1066 if (bp->status & BOARD_ACTIVE) {
1067 spin_unlock_irqrestore(&bp->card_lock, flags);
1068 return;
1069 }
1070 port = bp->ports;
1071 bp->status |= BOARD_ACTIVE;
1072 spin_unlock_irqrestore(&bp->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001073 for (channel = 0; channel < bp->port_count; channel++, port++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 drop_dtr_rts(port);
1075 return;
1076}
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001077
1078static int isicom_setup_port(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079{
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001080 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001082
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 if (port->flags & ASYNC_INITIALIZED) {
1084 return 0;
1085 }
1086 if (!port->xmit_buf) {
1087 unsigned long page;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001088
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 if (!(page = get_zeroed_page(GFP_KERNEL)))
1090 return -ENOMEM;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001091
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 if (port->xmit_buf) {
1093 free_page(page);
1094 return -ERESTARTSYS;
1095 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001096 port->xmit_buf = (unsigned char *) page;
1097 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099 spin_lock_irqsave(&card->card_lock, flags);
1100 if (port->tty)
1101 clear_bit(TTY_IO_ERROR, &port->tty->flags);
1102 if (port->count == 1)
1103 card->count++;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001104
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 /* discard any residual data */
1108 kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 isicom_config_port(port);
1111 port->flags |= ASYNC_INITIALIZED;
1112 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001113
1114 return 0;
1115}
1116
1117static int block_til_ready(struct tty_struct *tty, struct file *filp, struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118{
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001119 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 int do_clocal = 0, retval;
1121 unsigned long flags;
1122 DECLARE_WAITQUEUE(wait, current);
1123
1124 /* block if port is in the process of being closed */
1125
1126 if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001127 pr_dbg("block_til_ready: close in progress.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 interruptible_sleep_on(&port->close_wait);
1129 if (port->flags & ASYNC_HUP_NOTIFY)
1130 return -EAGAIN;
1131 else
1132 return -ERESTARTSYS;
1133 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001134
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 /* if non-blocking mode is set ... */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001136
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001138 pr_dbg("block_til_ready: non-block mode.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 port->flags |= ASYNC_NORMAL_ACTIVE;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001140 return 0;
1141 }
1142
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 if (C_CLOCAL(tty))
1144 do_clocal = 1;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001145
1146 /* block waiting for DCD to be asserted, and while
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 callout dev is busy */
1148 retval = 0;
1149 add_wait_queue(&port->open_wait, &wait);
1150
1151 spin_lock_irqsave(&card->card_lock, flags);
1152 if (!tty_hung_up_p(filp))
1153 port->count--;
1154 port->blocked_open++;
1155 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001156
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157 while (1) {
1158 raise_dtr_rts(port);
1159
1160 set_current_state(TASK_INTERRUPTIBLE);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001161 if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (port->flags & ASYNC_HUP_NOTIFY)
1163 retval = -EAGAIN;
1164 else
1165 retval = -ERESTARTSYS;
1166 break;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 if (!(port->flags & ASYNC_CLOSING) &&
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001169 (do_clocal || (port->status & ISI_DCD))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 break;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001171 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 if (signal_pending(current)) {
1173 retval = -ERESTARTSYS;
1174 break;
1175 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001176 schedule();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 }
1178 set_current_state(TASK_RUNNING);
1179 remove_wait_queue(&port->open_wait, &wait);
1180 spin_lock_irqsave(&card->card_lock, flags);
1181 if (!tty_hung_up_p(filp))
1182 port->count++;
1183 port->blocked_open--;
1184 spin_unlock_irqrestore(&card->card_lock, flags);
1185 if (retval)
1186 return retval;
1187 port->flags |= ASYNC_NORMAL_ACTIVE;
1188 return 0;
1189}
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001190
1191static int isicom_open(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001193 struct isi_port *port;
1194 struct isi_board *card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 unsigned int line, board;
1196 int error;
1197
1198 line = tty->index;
1199 if (line < 0 || line > PORT_COUNT-1)
1200 return -ENODEV;
1201 board = BOARD(line);
1202 card = &isi_card[board];
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001203
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 if (!(card->status & FIRMWARE_LOADED))
1205 return -ENODEV;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001206
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 /* open on a port greater than the port count for the card !!! */
1208 if (line > ((board * 16) + card->port_count - 1))
1209 return -ENODEV;
1210
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001211 port = &isi_ports[line];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if (isicom_paranoia_check(port, tty->name, "isicom_open"))
1213 return -ENODEV;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001214
1215 isicom_setup_board(card);
1216
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 port->count++;
1218 tty->driver_data = port;
1219 port->tty = tty;
1220 if ((error = isicom_setup_port(port))!=0)
1221 return error;
1222 if ((error = block_til_ready(tty, filp, port))!=0)
1223 return error;
1224
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001225 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226}
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001227
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228/* close et all */
1229
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001230static inline void isicom_shutdown_board(struct isi_board *bp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
1232 unsigned long flags;
1233
1234 spin_lock_irqsave(&bp->card_lock, flags);
1235 if (bp->status & BOARD_ACTIVE) {
1236 bp->status &= ~BOARD_ACTIVE;
1237 }
1238 spin_unlock_irqrestore(&bp->card_lock, flags);
1239}
1240
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001241static void isicom_shutdown_port(struct isi_port *port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242{
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001243 struct isi_board *card = port->card;
1244 struct tty_struct *tty;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001246
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 tty = port->tty;
1248
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001249 spin_lock_irqsave(&card->card_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 if (!(port->flags & ASYNC_INITIALIZED)) {
1251 spin_unlock_irqrestore(&card->card_lock, flags);
1252 return;
1253 }
1254 if (port->xmit_buf) {
1255 free_page((unsigned long) port->xmit_buf);
1256 port->xmit_buf = NULL;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 port->flags &= ~ASYNC_INITIALIZED;
1259 /* 3rd October 2000 : Vinayak P Risbud */
1260 port->tty = NULL;
1261 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001262
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 /*Fix done by Anil .S on 30-04-2001
1264 remote login through isi port has dtr toggle problem
1265 due to which the carrier drops before the password prompt
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001266 appears on the remote end. Now we drop the dtr only if the
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 HUPCL(Hangup on close) flag is set for the tty*/
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001268
1269 if (C_HUPCL(tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 /* drop dtr on this port */
1271 drop_dtr(port);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001272
1273 /* any other port uninits */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 if (tty)
1275 set_bit(TTY_IO_ERROR, &tty->flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001276
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 if (--card->count < 0) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001278 pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 card->base, card->count);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001280 card->count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001282
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 /* last port was closed , shutdown that boad too */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001284 if (C_HUPCL(tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 if (!card->count)
1286 isicom_shutdown_board(card);
1287 }
1288}
1289
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001290static void isicom_close(struct tty_struct *tty, struct file *filp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291{
Jiri Slaby8070e352006-01-09 20:54:22 -08001292 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001293 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001295
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 if (!port)
1297 return;
1298 if (isicom_paranoia_check(port, tty->name, "isicom_close"))
1299 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001300
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001301 pr_dbg("Close start!!!.\n");
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001302
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 spin_lock_irqsave(&card->card_lock, flags);
1304 if (tty_hung_up_p(filp)) {
1305 spin_unlock_irqrestore(&card->card_lock, flags);
1306 return;
1307 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001308
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 if (tty->count == 1 && port->count != 1) {
Jiri Slaby8070e352006-01-09 20:54:22 -08001310 printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port count"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 "tty->count = 1 port count = %d.\n",
1312 card->base, port->count);
1313 port->count = 1;
1314 }
1315 if (--port->count < 0) {
Jiri Slaby8070e352006-01-09 20:54:22 -08001316 printk(KERN_WARNING "ISICOM:(0x%lx) isicom_close: bad port count for"
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001317 "channel%d = %d", card->base, port->channel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 port->count);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001319 port->count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 if (port->count) {
1323 spin_unlock_irqrestore(&card->card_lock, flags);
1324 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001325 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 port->flags |= ASYNC_CLOSING;
1327 tty->closing = 1;
1328 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
1331 tty_wait_until_sent(tty, port->closing_wait);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001332 /* indicate to the card that no more data can be received
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 on this port */
1334 spin_lock_irqsave(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001335 if (port->flags & ASYNC_INITIALIZED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 card->port_status &= ~(1 << port->channel);
1337 outw(card->port_status, card->base + 0x02);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 isicom_shutdown_port(port);
1340 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001341
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (tty->driver->flush_buffer)
1343 tty->driver->flush_buffer(tty);
1344 tty_ldisc_flush(tty);
1345
1346 spin_lock_irqsave(&card->card_lock, flags);
1347 tty->closing = 0;
1348
1349 if (port->blocked_open) {
1350 spin_unlock_irqrestore(&card->card_lock, flags);
1351 if (port->close_delay) {
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001352 pr_dbg("scheduling until time out.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 msleep_interruptible(jiffies_to_msecs(port->close_delay));
1354 }
1355 spin_lock_irqsave(&card->card_lock, flags);
1356 wake_up_interruptible(&port->open_wait);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001357 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
1359 wake_up_interruptible(&port->close_wait);
1360 spin_unlock_irqrestore(&card->card_lock, flags);
1361}
1362
1363/* write et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001364static int isicom_write(struct tty_struct *tty, const unsigned char *buf,
1365 int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366{
Jiri Slaby8070e352006-01-09 20:54:22 -08001367 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001368 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 unsigned long flags;
1370 int cnt, total = 0;
1371
1372 if (isicom_paranoia_check(port, tty->name, "isicom_write"))
1373 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001374
Jiri Slaby9ac09482006-01-09 20:54:24 -08001375 if (!tty || !port->xmit_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001377
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 spin_lock_irqsave(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001379
1380 while(1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 cnt = min_t(int, count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001382 SERIAL_XMIT_SIZE - port->xmit_head));
1383 if (cnt <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 break;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001385
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 memcpy(port->xmit_buf + port->xmit_head, buf, cnt);
1387 port->xmit_head = (port->xmit_head + cnt) & (SERIAL_XMIT_SIZE - 1);
1388 port->xmit_cnt += cnt;
1389 buf += cnt;
1390 count -= cnt;
1391 total += cnt;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001392 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped)
1394 port->status |= ISI_TXOK;
1395 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001396 return total;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397}
1398
1399/* put_char et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001400static void isicom_put_char(struct tty_struct *tty, unsigned char ch)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401{
Jiri Slaby8070e352006-01-09 20:54:22 -08001402 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001403 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001405
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 if (isicom_paranoia_check(port, tty->name, "isicom_put_char"))
1407 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001408
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 if (!tty || !port->xmit_buf)
1410 return;
1411
1412 spin_lock_irqsave(&card->card_lock, flags);
1413 if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
1414 spin_unlock_irqrestore(&card->card_lock, flags);
1415 return;
1416 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 port->xmit_buf[port->xmit_head++] = ch;
1419 port->xmit_head &= (SERIAL_XMIT_SIZE - 1);
1420 port->xmit_cnt++;
1421 spin_unlock_irqrestore(&card->card_lock, flags);
1422}
1423
1424/* flush_chars et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001425static void isicom_flush_chars(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426{
Jiri Slaby8070e352006-01-09 20:54:22 -08001427 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 if (isicom_paranoia_check(port, tty->name, "isicom_flush_chars"))
1430 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001431
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !port->xmit_buf)
1433 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 /* this tells the transmitter to consider this port for
1436 data output to the card ... that's the best we can do. */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001437 port->status |= ISI_TXOK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438}
1439
1440/* write_room et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001441static int isicom_write_room(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442{
Jiri Slaby8070e352006-01-09 20:54:22 -08001443 struct isi_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 int free;
1445
1446 if (isicom_paranoia_check(port, tty->name, "isicom_write_room"))
1447 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001448
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 free = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
1450 if (free < 0)
1451 free = 0;
1452 return free;
1453}
1454
1455/* chars_in_buffer et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001456static int isicom_chars_in_buffer(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457{
Jiri Slaby8070e352006-01-09 20:54:22 -08001458 struct isi_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 if (isicom_paranoia_check(port, tty->name, "isicom_chars_in_buffer"))
1460 return 0;
1461 return port->xmit_cnt;
1462}
1463
1464/* ioctl et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001465static inline void isicom_send_break(struct isi_port *port, unsigned long length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466{
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001467 struct isi_board *card = port->card;
Jiri Slaby8070e352006-01-09 20:54:22 -08001468 unsigned long base = card->base;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001469
1470 if (!lock_card(card))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001472
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
1474 outw((length & 0xff) << 8 | 0x00, base);
1475 outw((length & 0xff00), base);
1476 InterruptTheCard(base);
1477
1478 unlock_card(card);
1479}
1480
1481static int isicom_tiocmget(struct tty_struct *tty, struct file *file)
1482{
Jiri Slaby8070e352006-01-09 20:54:22 -08001483 struct isi_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 /* just send the port status */
Jiri Slaby8070e352006-01-09 20:54:22 -08001485 u16 status = port->status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
1488 return -ENODEV;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001489
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 return ((status & ISI_RTS) ? TIOCM_RTS : 0) |
1491 ((status & ISI_DTR) ? TIOCM_DTR : 0) |
1492 ((status & ISI_DCD) ? TIOCM_CAR : 0) |
1493 ((status & ISI_DSR) ? TIOCM_DSR : 0) |
1494 ((status & ISI_CTS) ? TIOCM_CTS : 0) |
1495 ((status & ISI_RI ) ? TIOCM_RI : 0);
1496}
1497
1498static int isicom_tiocmset(struct tty_struct *tty, struct file *file,
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001499 unsigned int set, unsigned int clear)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500{
Jiri Slaby8070e352006-01-09 20:54:22 -08001501 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001502
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
1504 return -ENODEV;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001505
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 if (set & TIOCM_RTS)
1507 raise_rts(port);
1508 if (set & TIOCM_DTR)
1509 raise_dtr(port);
1510
1511 if (clear & TIOCM_RTS)
1512 drop_rts(port);
1513 if (clear & TIOCM_DTR)
1514 drop_dtr(port);
1515
1516 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001517}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001519static int isicom_set_serial_info(struct isi_port *port,
1520 struct serial_struct __user *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521{
1522 struct serial_struct newinfo;
1523 int reconfig_port;
1524
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001525 if (copy_from_user(&newinfo, info, sizeof(newinfo)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 return -EFAULT;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001527
1528 reconfig_port = ((port->flags & ASYNC_SPD_MASK) !=
1529 (newinfo.flags & ASYNC_SPD_MASK));
1530
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 if (!capable(CAP_SYS_ADMIN)) {
1532 if ((newinfo.close_delay != port->close_delay) ||
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001533 (newinfo.closing_wait != port->closing_wait) ||
1534 ((newinfo.flags & ~ASYNC_USR_MASK) !=
1535 (port->flags & ~ASYNC_USR_MASK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 return -EPERM;
1537 port->flags = ((port->flags & ~ ASYNC_USR_MASK) |
1538 (newinfo.flags & ASYNC_USR_MASK));
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001539 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 else {
1541 port->close_delay = newinfo.close_delay;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001542 port->closing_wait = newinfo.closing_wait;
1543 port->flags = ((port->flags & ~ASYNC_FLAGS) |
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 (newinfo.flags & ASYNC_FLAGS));
1545 }
1546 if (reconfig_port) {
1547 isicom_config_port(port);
1548 }
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001549 return 0;
1550}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001552static int isicom_get_serial_info(struct isi_port *port,
1553 struct serial_struct __user *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554{
1555 struct serial_struct out_info;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001556
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 memset(&out_info, 0, sizeof(out_info));
1558/* out_info.type = ? */
1559 out_info.line = port - isi_ports;
1560 out_info.port = port->card->base;
1561 out_info.irq = port->card->irq;
1562 out_info.flags = port->flags;
1563/* out_info.baud_base = ? */
1564 out_info.close_delay = port->close_delay;
1565 out_info.closing_wait = port->closing_wait;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001566 if (copy_to_user(info, &out_info, sizeof(out_info)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 return -EFAULT;
1568 return 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001569}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001571static int isicom_ioctl(struct tty_struct *tty, struct file *filp,
1572 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573{
Jiri Slaby8070e352006-01-09 20:54:22 -08001574 struct isi_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 void __user *argp = (void __user *)arg;
1576 int retval;
1577
1578 if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
1579 return -ENODEV;
1580
1581 switch(cmd) {
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001582 case TCSBRK:
1583 retval = tty_check_change(tty);
1584 if (retval)
1585 return retval;
1586 tty_wait_until_sent(tty, 0);
1587 if (!arg)
1588 isicom_send_break(port, HZ/4);
1589 return 0;
1590
1591 case TCSBRKP:
1592 retval = tty_check_change(tty);
1593 if (retval)
1594 return retval;
1595 tty_wait_until_sent(tty, 0);
1596 isicom_send_break(port, arg ? arg * (HZ/10) : HZ/4);
1597 return 0;
1598
1599 case TIOCGSOFTCAR:
1600 return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *)argp);
1601
1602 case TIOCSSOFTCAR:
1603 if (get_user(arg, (unsigned long __user *) argp))
1604 return -EFAULT;
1605 tty->termios->c_cflag =
1606 ((tty->termios->c_cflag & ~CLOCAL) |
1607 (arg ? CLOCAL : 0));
1608 return 0;
1609
1610 case TIOCGSERIAL:
1611 return isicom_get_serial_info(port, argp);
1612
1613 case TIOCSSERIAL:
1614 return isicom_set_serial_info(port, argp);
1615
1616 default:
1617 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 }
1619 return 0;
1620}
1621
1622/* set_termios et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001623static void isicom_set_termios(struct tty_struct *tty,
1624 struct termios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625{
Jiri Slaby8070e352006-01-09 20:54:22 -08001626 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001627
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
1629 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001630
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631 if (tty->termios->c_cflag == old_termios->c_cflag &&
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001632 tty->termios->c_iflag == old_termios->c_iflag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001634
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 isicom_config_port(port);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001636
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637 if ((old_termios->c_cflag & CRTSCTS) &&
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001638 !(tty->termios->c_cflag & CRTSCTS)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 tty->hw_stopped = 0;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001640 isicom_start(tty);
1641 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642}
1643
1644/* throttle et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001645static void isicom_throttle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646{
Jiri Slaby8070e352006-01-09 20:54:22 -08001647 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001648 struct isi_board *card = port->card;
1649
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650 if (isicom_paranoia_check(port, tty->name, "isicom_throttle"))
1651 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001652
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 /* tell the card that this port cannot handle any more data for now */
1654 card->port_status &= ~(1 << port->channel);
1655 outw(card->port_status, card->base + 0x02);
1656}
1657
1658/* unthrottle et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001659static void isicom_unthrottle(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660{
Jiri Slaby8070e352006-01-09 20:54:22 -08001661 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001662 struct isi_board *card = port->card;
1663
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 if (isicom_paranoia_check(port, tty->name, "isicom_unthrottle"))
1665 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 /* tell the card that this port is ready to accept more data */
1668 card->port_status |= (1 << port->channel);
1669 outw(card->port_status, card->base + 0x02);
1670}
1671
1672/* stop et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001673static void isicom_stop(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674{
Jiri Slaby8070e352006-01-09 20:54:22 -08001675 struct isi_port *port = tty->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
1677 if (isicom_paranoia_check(port, tty->name, "isicom_stop"))
1678 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001679
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 /* this tells the transmitter not to consider this port for
1681 data output to the card. */
1682 port->status &= ~ISI_TXOK;
1683}
1684
1685/* start et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001686static void isicom_start(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687{
Jiri Slaby8070e352006-01-09 20:54:22 -08001688 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001689
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 if (isicom_paranoia_check(port, tty->name, "isicom_start"))
1691 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001692
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 /* this tells the transmitter to consider this port for
1694 data output to the card. */
1695 port->status |= ISI_TXOK;
1696}
1697
1698/* hangup et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001699static void do_isicom_hangup(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700{
Jiri Slaby8070e352006-01-09 20:54:22 -08001701 struct isi_port *port = data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001702 struct tty_struct *tty;
1703
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 tty = port->tty;
1705 if (tty)
1706 tty_hangup(tty);
1707}
1708
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001709static void isicom_hangup(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710{
Jiri Slaby8070e352006-01-09 20:54:22 -08001711 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001712
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
1714 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001715
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 isicom_shutdown_port(port);
1717 port->count = 0;
1718 port->flags &= ~ASYNC_NORMAL_ACTIVE;
1719 port->tty = NULL;
1720 wake_up_interruptible(&port->open_wait);
1721}
1722
1723/* flush_buffer et all */
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001724static void isicom_flush_buffer(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725{
Jiri Slaby8070e352006-01-09 20:54:22 -08001726 struct isi_port *port = tty->driver_data;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001727 struct isi_board *card = port->card;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 unsigned long flags;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001729
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 if (isicom_paranoia_check(port, tty->name, "isicom_flush_buffer"))
1731 return;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001732
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 spin_lock_irqsave(&card->card_lock, flags);
1734 port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
1735 spin_unlock_irqrestore(&card->card_lock, flags);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001736
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 wake_up_interruptible(&tty->write_wait);
1738 tty_wakeup(tty);
1739}
1740
Jiri Slaby9ac09482006-01-09 20:54:24 -08001741/*
1742 * Driver init and deinit functions
1743 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744
Jiri Slaby9ac09482006-01-09 20:54:24 -08001745static int __devinit isicom_register_ioregion(struct pci_dev *pdev,
1746 const unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747{
Jiri Slaby9ac09482006-01-09 20:54:24 -08001748 struct isi_board *board = pci_get_drvdata(pdev);
1749
1750 if (!board->base)
1751 return -EINVAL;
1752
1753 if (!request_region(board->base, 16, ISICOM_NAME)) {
1754 dev_dbg(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d "
1755 "will be disabled.\n", board->base, board->base + 15,
1756 index + 1);
1757 return -EBUSY;
1758 }
1759
1760 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761}
1762
Jiri Slaby9ac09482006-01-09 20:54:24 -08001763static void isicom_unregister_ioregion(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764{
Jiri Slaby9ac09482006-01-09 20:54:24 -08001765 struct isi_board *board = pci_get_drvdata(pdev);
1766
1767 if (!board->base)
1768 return;
1769
1770 release_region(board->base, 16);
1771 dev_dbg(&pdev->dev, "I/O Region 0x%lx-0x%lx released.\n",
1772 board->base, board->base + 15);
1773 board->base = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774}
1775
1776static struct tty_operations isicom_ops = {
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001777 .open = isicom_open,
1778 .close = isicom_close,
1779 .write = isicom_write,
1780 .put_char = isicom_put_char,
1781 .flush_chars = isicom_flush_chars,
1782 .write_room = isicom_write_room,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 .chars_in_buffer = isicom_chars_in_buffer,
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001784 .ioctl = isicom_ioctl,
1785 .set_termios = isicom_set_termios,
1786 .throttle = isicom_throttle,
1787 .unthrottle = isicom_unthrottle,
1788 .stop = isicom_stop,
1789 .start = isicom_start,
1790 .hangup = isicom_hangup,
1791 .flush_buffer = isicom_flush_buffer,
1792 .tiocmget = isicom_tiocmget,
1793 .tiocmset = isicom_tiocmset,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794};
1795
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001796static int __devinit isicom_register_tty_driver(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797{
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001798 int error = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799
1800 /* tty driver structure initialization */
1801 isicom_normal = alloc_tty_driver(PORT_COUNT);
1802 if (!isicom_normal)
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001803 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001805 isicom_normal->owner = THIS_MODULE;
1806 isicom_normal->name = "ttyM";
1807 isicom_normal->devfs_name = "isicom/";
1808 isicom_normal->major = ISICOM_NMAJOR;
1809 isicom_normal->minor_start = 0;
1810 isicom_normal->type = TTY_DRIVER_TYPE_SERIAL;
1811 isicom_normal->subtype = SERIAL_TYPE_NORMAL;
1812 isicom_normal->init_termios = tty_std_termios;
1813 isicom_normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL |
1814 CLOCAL;
1815 isicom_normal->flags = TTY_DRIVER_REAL_RAW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 tty_set_operations(isicom_normal, &isicom_ops);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001817
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001818 if ((error = tty_register_driver(isicom_normal))) {
1819 pr_dbg("Couldn't register the dialin driver, error=%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820 error);
1821 put_tty_driver(isicom_normal);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 }
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001823end:
1824 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825}
1826
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001827static void isicom_unregister_tty_driver(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828{
Jiri Slabyaaa246e2006-01-09 20:54:23 -08001829 int error;
1830
1831 if ((error = tty_unregister_driver(isicom_normal)))
1832 pr_dbg("couldn't unregister normal driver, error=%d.\n", error);
1833
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 put_tty_driver(isicom_normal);
1835}
1836
Jiri Slaby9ac09482006-01-09 20:54:24 -08001837static int __devinit isicom_register_isr(struct pci_dev *pdev,
1838 const unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839{
Jiri Slaby9ac09482006-01-09 20:54:24 -08001840 struct isi_board *board = pci_get_drvdata(pdev);
1841 unsigned long irqflags = SA_INTERRUPT;
1842 int retval = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
Jiri Slaby9ac09482006-01-09 20:54:24 -08001844 if (!board->base)
1845 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846
Jiri Slaby9ac09482006-01-09 20:54:24 -08001847 if (board->isa == NO)
1848 irqflags |= SA_SHIRQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
Jiri Slaby9ac09482006-01-09 20:54:24 -08001850 retval = request_irq(board->irq, isicom_interrupt, irqflags,
1851 ISICOM_NAME, board);
1852 if (retval < 0)
1853 dev_warn(&pdev->dev, "Could not install handler at Irq %d. "
1854 "Card%d will be disabled.\n", board->irq, index + 1);
1855 else
1856 retval = 0;
1857end:
1858 return retval;
1859}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860
Jiri Slaby9ac09482006-01-09 20:54:24 -08001861static int __devinit reset_card(struct pci_dev *pdev,
1862 const unsigned int card, unsigned int *signature)
1863{
1864 struct isi_board *board = pci_get_drvdata(pdev);
1865 unsigned long base = board->base;
1866 unsigned int portcount = 0;
1867 int retval = 0;
1868
1869 dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1,
1870 base);
1871
1872 inw(base + 0x8);
1873
1874 mdelay(10);
1875
1876 outw(0, base + 0x8); /* Reset */
1877
1878 msleep(3000);
1879
1880 *signature = inw(base + 0x4) & 0xff;
1881
1882 if (board->isa == YES) {
1883 if (!(inw(base + 0xe) & 0x1) || (inw(base + 0x2))) {
1884 dev_dbg(&pdev->dev, "base+0x2=0x%lx, base+0xe=0x%lx\n",
1885 inw(base + 0x2), inw(base + 0xe));
1886 dev_err(&pdev->dev, "ISILoad:ISA Card%d reset failure "
1887 "(Possible bad I/O Port Address 0x%lx).\n",
1888 card + 1, base);
1889 retval = -EIO;
1890 goto end;
1891 }
1892 } else {
1893 portcount = inw(base + 0x2);
1894 if (!(inw(base + 0xe) & 0x1) || ((portcount != 0) &&
1895 (portcount != 4) && (portcount != 8))) {
1896 dev_dbg(&pdev->dev, "base+0x2=0x%lx, base+0xe=0x%lx\n",
1897 inw(base + 0x2), inw(base + 0xe));
1898 dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure "
1899 "(Possible bad I/O Port Address 0x%lx).\n",
1900 card + 1, base);
1901 retval = -EIO;
1902 goto end;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08001903 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905
Jiri Slaby9ac09482006-01-09 20:54:24 -08001906 switch (*signature) {
1907 case 0xa5:
1908 case 0xbb:
1909 case 0xdd:
1910 board->port_count = (board->isa == NO && portcount == 4) ? 4 :
1911 8;
1912 board->shift_count = 12;
1913 break;
1914 case 0xcc:
1915 board->port_count = 16;
1916 board->shift_count = 11;
1917 break;
1918 default:
1919 dev_warn(&pdev->dev, "ISILoad:Card%d reset failure (Possible "
1920 "bad I/O Port Address 0x%lx).\n", card + 1, base);
1921 dev_dbg(&pdev->dev, "Sig=0x%lx\n", signature);
1922 retval = -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923 }
Jiri Slaby9ac09482006-01-09 20:54:24 -08001924 dev_info(&pdev->dev, "-Done\n");
1925
1926end:
1927 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928}
1929
Jiri Slaby9ac09482006-01-09 20:54:24 -08001930/*
1931 * Insmod can set static symbols so keep these static
1932 */
1933static int io[4];
1934static int irq[4];
1935static int card;
1936
1937static int __devinit isicom_probe(struct pci_dev *pdev,
1938 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Jiri Slaby9ac09482006-01-09 20:54:24 -08001940 unsigned int ioaddr, signature, index;
1941 int retval = -EPERM;
1942 u8 pciirq;
1943 struct isi_board *board = NULL;
1944
1945 if (card >= BOARD_COUNT)
1946 goto err;
1947
1948 ioaddr = pci_resource_start(pdev, 3);
1949 /* i.e at offset 0x1c in the PCI configuration register space. */
1950 pciirq = pdev->irq;
1951 dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);
1952
1953 /* allot the first empty slot in the array */
1954 for (index = 0; index < BOARD_COUNT; index++)
1955 if (isi_card[index].base == 0) {
1956 board = &isi_card[index];
1957 break;
1958 }
1959
1960 board->base = ioaddr;
1961 board->irq = pciirq;
1962 board->isa = NO;
1963 card++;
1964
1965 pci_set_drvdata(pdev, board);
1966
1967 retval = isicom_register_ioregion(pdev, index);
1968 if (retval < 0)
1969 goto err;
1970
1971 retval = isicom_register_isr(pdev, index);
1972 if (retval < 0)
1973 goto errunrr;
1974
1975 retval = reset_card(pdev, index, &signature);
1976 if (retval < 0)
1977 goto errunri;
1978
1979 return 0;
1980
1981errunri:
1982 free_irq(board->irq, board);
1983errunrr:
1984 isicom_unregister_ioregion(pdev);
1985err:
1986 board->base = 0;
1987 return retval;
1988}
1989
1990static void __devexit isicom_remove(struct pci_dev *pdev)
1991{
1992 struct isi_board *board = pci_get_drvdata(pdev);
1993
1994 free_irq(board->irq, board);
1995 isicom_unregister_ioregion(pdev);
1996}
1997
1998static int __devinit isicom_setup(void)
1999{
2000 int retval, idx, channel;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002001 struct isi_port *port;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002002
Jiri Slaby9ac09482006-01-09 20:54:24 -08002003 card = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 memset(isi_ports, 0, sizeof(isi_ports));
Jiri Slaby9ac09482006-01-09 20:54:24 -08002005
2006 for(idx = 0; idx < BOARD_COUNT; idx++) {
2007 port = &isi_ports[idx * 16];
2008 isi_card[idx].ports = port;
2009 spin_lock_init(&isi_card[idx].card_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 for (channel = 0; channel < 16; channel++, port++) {
2011 port->magic = ISICOM_MAGIC;
Jiri Slaby9ac09482006-01-09 20:54:24 -08002012 port->card = &isi_card[idx];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 port->channel = channel;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002014 port->close_delay = 50 * HZ/100;
2015 port->closing_wait = 3000 * HZ/100;
2016 INIT_WORK(&port->hangup_tq, do_isicom_hangup, port);
2017 INIT_WORK(&port->bh_tqueue, isicom_bottomhalf, port);
2018 port->status = 0;
2019 init_waitqueue_head(&port->open_wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 init_waitqueue_head(&port->close_wait);
2021 /* . . . */
Jiri Slaby9ac09482006-01-09 20:54:24 -08002022 }
2023 isi_card[idx].base = 0;
2024 isi_card[idx].irq = 0;
2025
2026 if (!io[idx])
2027 continue;
2028
2029 if (irq[idx] == 2 || irq[idx] == 3 || irq[idx] == 4 ||
2030 irq[idx] == 5 || irq[idx] == 7 ||
2031 irq[idx] == 10 || irq[idx] == 11 ||
2032 irq[idx] == 12 || irq[idx] == 15) {
2033 printk(KERN_ERR "ISICOM: ISA not supported yet.\n");
2034 retval = -EINVAL;
2035 goto error;
2036 } else
2037 printk(KERN_ERR "ISICOM: Irq %d unsupported. "
2038 "Disabling Card%d...\n", irq[idx], idx + 1);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002039 }
2040
Jiri Slaby9ac09482006-01-09 20:54:24 -08002041 retval = isicom_register_tty_driver();
2042 if (retval < 0)
2043 goto error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044
Jiri Slaby9ac09482006-01-09 20:54:24 -08002045 retval = pci_register_driver(&isicom_driver);
2046 if (retval < 0) {
2047 printk(KERN_ERR "ISICOM: Unable to register pci driver.\n");
2048 goto errtty;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002049 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050
2051 retval = misc_register(&isiloader_device);
Jiri Slaby9ac09482006-01-09 20:54:24 -08002052 if (retval < 0)
2053 goto errpci;
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002054
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 init_timer(&tx);
2056 tx.expires = jiffies + 1;
2057 tx.data = 0;
2058 tx.function = isicom_tx;
2059 re_schedule = 1;
2060 add_timer(&tx);
Jiri Slabyd8d16e42006-01-09 20:54:21 -08002061
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 return 0;
Jiri Slaby9ac09482006-01-09 20:54:24 -08002063errpci:
2064 pci_unregister_driver(&isicom_driver);
2065errtty:
2066 isicom_unregister_tty_driver();
2067error:
2068 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069}
2070
2071static void __exit isicom_exit(void)
2072{
Jiri Slabyaaa246e2006-01-09 20:54:23 -08002073 unsigned int index = 0;
2074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 re_schedule = 0;
Jiri Slabyaaa246e2006-01-09 20:54:23 -08002076
2077 while (re_schedule != 2 && index++ < 100)
2078 msleep(10);
2079
Jiri Slaby9ac09482006-01-09 20:54:24 -08002080 pci_unregister_driver(&isicom_driver);
Jiri Slabyaaa246e2006-01-09 20:54:23 -08002081 isicom_unregister_tty_driver();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082}
2083
2084module_init(isicom_setup);
2085module_exit(isicom_exit);
Jiri Slabyaaa246e2006-01-09 20:54:23 -08002086
2087MODULE_AUTHOR("MultiTech");
2088MODULE_DESCRIPTION("Driver for the ISI series of cards by MultiTech");
2089MODULE_LICENSE("GPL");
2090module_param_array(io, int, NULL, 0);
2091MODULE_PARM_DESC(io, "I/O ports for the cards");
2092module_param_array(irq, int, NULL, 0);
2093MODULE_PARM_DESC(irq, "Interrupts for the cards");