,wyEo>>4) /*
{L M Q * pc300_tty.c Cyclades-PC300(tm) TTY Driver.
C9>^!?> *
-KqMSf&9 * Author: Regina Kodato <
reginak@cyclades.com>
PevT`\> *
Ov.oyke4 * Copyright: (c) 1999-2002 Cyclades Corp.
7sVO?:bj} *
N)KN!! * This program is free software; you can redistribute it and/or
x
Y}.mP * modify it under the terms of the GNU General Public License
Scmew * as published by the Free Software Foundation; either version
Emk:@$3{r * 2 of the License, or (at your option) any later version.
T~?&hZ> *
H8Z|gq1r * $Log: pc300_tty.c,v $
91k-os(4] * Revision 3.7 2002/03/07 14:17:09 henrique
)cm^;(#pV * License data fixed
T[J8zLO *
&.Yu%=} * Revision 3.6 2001/12/10 12:29:42 regina
e8z?) 4T * Fix the MLPPP bug
$]^Io)}f@ *
u|Ng>lU * Revision 3.5 2001/10/31 11:20:05 regina
-"bC[ WN * automatic pppd starts
SzUH6|=.R= *
j& L@L.d * Revision 3.4 2001/08/06 12:01:51 regina
}0OQm?xh * problem in DSR_DE bit
@CC
6`D *
%V#? 1{ * Revision 3.3 2001/07/26 22:58:41 regina
RgzzbW * update EDA value
&uf|Le4 *
#"[EVF0%1D * Revision 3.2 2001/07/12 13:11:20 regina
0}g~69Z1= * bug fix - DCD-OFF in pc300 tty driver
m;>:mwU *
5hDPX\ * DMA transmission bug fix
}=u#,nDl>$ *
-L
wz
T * Revision 3.1 2001/06/22 13:13:02 regina
+zl[C * MLPPP implementation
lKS 2OOYC` *
$ao7pvU6 */
[-R[rF (/!zHq #include <linux/module.h>
iD>H{1 h #include <linux/kernel.h>
_gl1Qtv@rf #include <linux/errno.h>
++=jh6 #include <linux/string.h>
/?}2OCq #include <linux/init.h>
6Z@T
/"mU( #include <linux/netdevice.h>
Ejyo
oO45 #include <linux/spinlock.h>
&=wvlI52` #include <linux/slab.h>
SPtx_+ Q)S #include <linux/if.h>
zV"'-iP #include <linux/skbuff.h>
r<Q0zKW!jN /* TTY includes */
Qzv& #include <linux/tty.h>
`9acR>00$ #include <linux/tty_flip.h>
!=6 \70lJ #include <linux/serial.h>
-<(RYMk*) !y$+RA7\ #include <asm/io.h>
n
ON]YDg #include <asm/uaccess.h>
#+|0 o- }gd'pgN"t #include "pc300.h"
nB4+*=$E+- lLU8eHf\ /* defines and macros */
1L=)93,M /* TTY Global definitions */
R
pT7Nr #define CPC_TTY_NPORTS 8 /* maximum number of the sync tty connections */
+HG*T[%/ #define CPC_TTY_MAJOR CYCLADES_MAJOR
}|Bs|$q #define CPC_TTY_MINOR_START 240 /* minor of the first PC300 interface */
X-psao0tI` 4"\%/kG #define CPC_TTY_MAX_MTU 2000
iMQ0Sq-%1 dKa2_|k' /* tty interface state */
8wn{W_5a #define CPC_TTY_ST_IDLE 0
"h8fTB\7S\ #define CPC_TTY_ST_INIT 1 /* configured with MLPPP and up */
x1}Ono3"T #define CPC_TTY_ST_OPEN 2 /* opened by application */
v'r)d-T o3h>)4 #define CPC_TTY_LOCK(card,flags)\
7&w| do {\
#WAX&<m spin_lock_irqsave(&card->card_lock, flags); \
(]zi; } while (0)
>G As&\4hs W mx3@]< #define CPC_TTY_UNLOCK(card,flags) \
[c v!YE do {\
4^:$|\?] spin_unlock_irqrestore(&card->card_lock, flags); \
{P)O# } while (0)
R 'fEw3^ kr-5O0tmf //#define CPC_TTY_DBG(format,a...) printk(format,##a)
^@Z8_PZo #define CPC_TTY_DBG(format,a...)
aS~~*UHW dAy\IfZX= /* data structures */
)g KC}_h= typedef struct _st_cpc_rx_buf {
>=.3Vydi1 struct _st_cpc_rx_buf *next;
!-ZY_ int size;
FW{K[km^P unsigned char data[1];
QXgfjo } st_cpc_rx_buf;
t=fP^bJ {.J<^V struct st_cpc_rx_list {
v 7%}ey[ st_cpc_rx_buf *first;
Sf@xP.d st_cpc_rx_buf *last;
z:1t
vG };
s-~`Ao'
< Ty7)j]b"zl typedef struct _st_cpc_tty_area {
l+X\>, int state; /* state of the TTY interface */
VmRfnH" int num_open;
DhD##5a unsigned int tty_minor; /* minor this interface */
m)Wq*&,o volatile struct st_cpc_rx_list buf_rx; /* ptr. to reception buffer */
8q;
aCtei unsigned char* buf_tx; /* ptr. to transmission buffer */
kO$n0y5e pc300dev_t* pc300dev; /* ptr. to info struct in PC300 driver */
n^*,JL9@ unsigned char name[20]; /* interf. name + "-tty" */
!T
9CpIM% struct tty_struct *tty;
^)C# struct work_struct tty_tx_work; /* tx work - tx interrupt */
z#GSt
ZT struct work_struct tty_rx_work; /* rx work - rx interrupt */
.K`n;lVs } st_cpc_tty_area;
"<^n@=g'q }w8yYI /* TTY data structures */
G\^<MR| static struct tty_driver serial_drv;
Mc$rsqDz 5".bM8o /* local variables */
$RU K<JN$6 static st_cpc_tty_area cpc_tty_area[CPC_TTY_NPORTS];
c;zk{dP w\{#nrhYU static int cpc_tty_cnt = 0; /* number of intrfaces configured with MLPPP */
XL'\$f static int cpc_tty_unreg_flag = 0;
-Kcjnl92i ze21Uj1x* /* TTY functions prototype */
!!w(`kmn1 static int cpc_tty_open(struct tty_struct *tty, struct file *flip);
F/3L^k] static void cpc_tty_close(struct tty_struct *tty, struct file *flip);
}Z<Sca7 static int cpc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count);
r3KNRr@ static int cpc_tty_write_room(struct tty_struct *tty);
LXPO@2QF static int cpc_tty_chars_in_buffer(struct tty_struct *tty);
]Tg@wMgI static void cpc_tty_flush_buffer(struct tty_struct *tty);
# s7e/GdKb static void cpc_tty_hangup(struct tty_struct *tty);
v>N*f~n static void cpc_tty_rx_work(struct work_struct *work);
1b 2 static void cpc_tty_tx_work(struct work_struct *work);
};<?W){!H static int cpc_tty_send_to_card(pc300dev_t *dev,void *buf, int len);
C[d1n#@r static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx);
N">#fYix static void cpc_tty_signal_off(pc300dev_t *pc300dev, unsigned char);
t*H|*L#YR static void cpc_tty_signal_on(pc300dev_t *pc300dev, unsigned char);
eZ-fy,E kk4+>mk static int pc300_tiocmset(struct tty_struct *, unsigned int, unsigned int);
:"H?phk static int pc300_tiocmget(struct tty_struct *);
'2|P-/jU Sw5:T /* functions called by PC300 driver */
F^S]7{ void cpc_tty_init(pc300dev_t *dev);
[x]~G void cpc_tty_unregister_service(pc300dev_t *pc300dev);
(hg6<` void cpc_tty_receive(pc300dev_t *pc300dev);
1LAd5X void cpc_tty_trigger_poll(pc300dev_t *pc300dev);
oN%zpz;OR void cpc_tty_reset_var(void);
%d%?\jV b KWAd~8,mk /*
&\b( * PC300 TTY clear "signal"
O'{kNr{u */
[-\U)>MY(p static void cpc_tty_signal_off(pc300dev_t *pc300dev, unsigned char signal)
,np|KoG|M {
(:?bQA'Td pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan;
+{C)^!zBK pc300_t *card = (pc300_t *) pc300chan->card;
lyyf&?2 int ch = pc300chan->channel;
NL;sn" unsigned long flags;
*c&OAL] " Up(Vj@ CPC_TTY_DBG("%s-tty: Clear signal %x\n",
u0G
tzk pc300dev->dev->name, signal);
p<}y'7( CPC_TTY_LOCK(card, flags);
3la `S$c cpc_writeb(card->hw.scabase + M_REG(CTL,ch),
\NEk B&^n cpc_readb(card->hw.scabase+M_REG(CTL,ch))& signal);
g j]8/~lr CPC_TTY_UNLOCK(card,flags);
AO|1m$xf }
7YK6e kM&-t&7 /*
Aq$1#1J * PC300 TTY set "signal" to ON
cMnN} ' */
C=v+e%)x@ static void cpc_tty_signal_on(pc300dev_t *pc300dev, unsigned char signal)
"Z;({a$v {
O:pg+o& pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan;
DT)][V^w pc300_t *card = (pc300_t *) pc300chan->card;
N&kUTSd int ch = pc300chan->channel;
9F?-zn;2s unsigned long flags;
>5"e<mwD7d 'mTY56Yq CPC_TTY_DBG("%s-tty: Set signal %x\n",
pV_zePyOn pc300dev->dev->name, signal);
ZbjUOlE02 CPC_TTY_LOCK(card, flags);
z@pa;_ cpc_writeb(card->hw.scabase + M_REG(CTL,ch),
=5V7212 cpc_readb(card->hw.scabase+M_REG(CTL,ch))& ~signal);
?%Tx%
dB CPC_TTY_UNLOCK(card,flags);
JYA>Q& }
yXv@yn HW,v" BHYguS^qz static const struct tty_operations pc300_ops = {
UnYb}rF#% .open = cpc_tty_open,
,Z _@]D@ .close = cpc_tty_close,
@TX@78fWz= .write = cpc_tty_write,
*" C9F/R .write_room = cpc_tty_write_room,
-)3+/4Q( .chars_in_buffer = cpc_tty_chars_in_buffer,
96QY0
.tiocmset = pc300_tiocmset,
b4bd^nrqV .tiocmget = pc300_tiocmget,
47Bg[ .flush_buffer = cpc_tty_flush_buffer,
F4WX$;1 .hangup = cpc_tty_hangup,
JtxVF!v };
\AA9
m'BZ {;& U5<NO rqdN%=C /*
y
5=rr3%v * PC300 TTY initialization routine
"::2]3e *
wVnmT94 * This routine is called by the PC300 driver during board configuration
W*CRxGyZCl * (ioctl=SIOCSP300CONF). At this point the adapter is completely
zwJ&K;"y( * initialized.
K[0z$T\
* o verify kernel version (only 2.4.x)
?wCX:?g * o register TTY driver
Zv=pS
(9 * o init cpc_tty_area struct
e@TwZ6l */
ztX$kX:_m void cpc_tty_init(pc300dev_t *pc300dev)
!6RDq` {
{=mGXd`x?l unsigned long port;
yt="kZ int aux;
knph549 st_cpc_tty_area * cpc_tty;
,+6u6 qGEp 6b H /* hdlcX - X=interface number */
-T{2R:\{ port = pc300dev->dev->name[4] - '0';
j>:N0:
if (port >= CPC_TTY_NPORTS) {
l'wu- printk("%s-tty: invalid interface selected (0-%i): %li",
zNoFM/1Vb pc300dev->dev->name,
~LV]cX2J( CPC_TTY_NPORTS-1,port);
yt5<J-m return;
m*N8!1Ot }
PlLt^q.z[ .Wy' if (cpc_tty_cnt == 0) { /* first TTY connection -> register driver */
'ROz| iJ CPC_TTY_DBG("%s-tty: driver init, major:%i, minor range:%i=%i\n",
\R]2YY`EP pc300dev->dev->name,
Og1vD5a CPC_TTY_MAJOR, CPC_TTY_MINOR_START,
5V =mj+X? CPC_TTY_MINOR_START+CPC_TTY_NPORTS);
7H{1i /* initialize tty driver struct */
su1fsoL0 memset(&serial_drv,0,sizeof(struct tty_driver));
t[>UAr1Vt serial_drv.magic = TTY_DRIVER_MAGIC;
(PGw{_ serial_drv.owner = THIS_MODULE;
6o3#<ap< serial_drv.driver_name = "pc300_tty";
(B\
UZb serial_drv.name = "ttyCP";
jaKW[@< serial_drv.major = CPC_TTY_MAJOR;
@P75f5p}< serial_drv.minor_start = CPC_TTY_MINOR_START;
42"nbJ serial_drv.num = CPC_TTY_NPORTS;
R5^6Kwu serial_drv.type = TTY_DRIVER_TYPE_SERIAL;
SE^l`.U@ serial_drv.subtype = SERIAL_TYPE_NORMAL;
.IdbaH
_a !3k-' ),z& serial_drv.init_termios = tty_std_termios;
7_l
Wr serial_drv.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
9wldd*r serial_drv.flags = TTY_DRIVER_REAL_RAW;
v^t7)nx^ &;P\e /* interface routines from the upper tty layer to the tty driver */
5=|h~/.k tty_set_operations(&serial_drv, &pc300_ops);
qmFbq<& 2-8Dc4H]r /* register the TTY driver */
=d^hiR!GN if (tty_register_driver(&serial_drv)) {
GU2TQx{V printk("%s-tty: Failed to register serial driver! ",
~Hub\kn pc300dev->dev->name);
`VO;\s$5j return;
ly[dV.<P }
:dULsl$Nz 3I~.'>Pd memset((void *)cpc_tty_area, 0,
X 5
or5v sizeof(st_cpc_tty_area) * CPC_TTY_NPORTS);
5!qf{4j }
!'F1Ht ZlMT) ~fM& cpc_tty = &cpc_tty_area[port];
dEKu5GI tNzO1BK if (cpc_tty->state != CPC_TTY_ST_IDLE) {
ut560,h~ CPC_TTY_DBG("%s-tty: TTY port %i, already in use.\n",
S!=R\_{u$ pc300dev->dev->name, port);
{fHor return;
,;w~ VZ4 }
Nr2,m"R{ }Cw,m0KV/ cpc_tty_cnt++;
l~]] RgU cpc_tty->state = CPC_TTY_ST_INIT;
v:/!OvLe cpc_tty->num_open= 0;
7R:Ij[dV cpc_tty->tty_minor = port + CPC_TTY_MINOR_START;
U{oM*[ cpc_tty->pc300dev = pc300dev;
IA.7If&k DAWF
=p] INIT_WORK(&cpc_tty->tty_tx_work, cpc_tty_tx_work);
"%^_.Db>| INIT_WORK(&cpc_tty->tty_rx_work, cpc_tty_rx_work);
W5`p Qdk k@|px#kq cpc_tty->buf_rx.first = cpc_tty->buf_rx.last = NULL;
wW\@^5 54>0Dv??H pc300dev->cpc_tty = (void *)cpc_tty;
jwE= H2:
Zda# aux = strlen(pc300dev->dev->name);
q/I( e memcpy(cpc_tty->name, pc300dev->dev->name, aux);
*|\bS " memcpy(&cpc_tty->name[aux], "-tty", 5);
+39uKOrZ 0Pf88 '6 cpc_open(pc300dev->dev);
B?8*-0a'[ cpc_tty_signal_off(pc300dev, CTL_DTR);
w$f_z*/ 6X h7Bx1 CPC_TTY_DBG("%s: Initializing TTY Sync Driver, tty major#%d minor#%i\n",
?|W3RK; cpc_tty->name,CPC_TTY_MAJOR,cpc_tty->tty_minor);
,s3| return;
PL$XXj>|: }
/K&9c
!]$C u]Vt>Ywu /*
)?#K0o[< * PC300 TTY OPEN routine
0:[A4S`X *
{*O+vtir% * This routine is called by the tty driver to open the interface
mm:TR?^ * o verify minor
zu
@|"f^` * o allocate buffer to Rx and Tx
Ka(B&. */
c{y'&3\
static int cpc_tty_open(struct tty_struct *tty, struct file *flip)
)$E){(Aa {
U3:|!CC)T int port ;
V>)/z|[ st_cpc_tty_area *cpc_tty;
#`|Nm3b G2I%^.s if (!tty) {
cK]n"6N[ return -ENODEV;
v%*don }
"0;WYw? ma*#*4 port = tty->index;
mq4Zy3H o}KVT%} if ((port < 0) || (port >= CPC_TTY_NPORTS)){
p )JR5z CPC_TTY_DBG("pc300_tty: open invalid port %d\n", port);
#bt f|\D return -ENODEV;
p! :oT1U }
^|Fy!kp us|Hb cpc_tty = &cpc_tty_area[port];
sd%)g<t COHBjufmR if (cpc_tty->state == CPC_TTY_ST_IDLE){
Posz|u<x CPC_TTY_DBG("%s: open - invalid interface, port=%d\n",
>e6 OlIW cpc_tty->name, tty->index);
+0%r@hTv&> return -ENODEV;
XTF[4#WO }
klQmo30i =bD.5,F) if (cpc_tty->num_open == 0) { /* first open of this tty */
oA-,>:}g{ if (!cpc_tty_area[port].buf_tx){
l]zQSXip cpc_tty_area[port].buf_tx = kmalloc(CPC_TTY_MAX_MTU,GFP_KERNEL);
I[K4/91 if (!cpc_tty_area[port].buf_tx) {
au50%sA~
CPC_TTY_DBG("%s: error in memory allocation\n",cpc_tty->name);
bskoi;)u return -ENOMEM;
nrev!h }
lJFy(^KQG, }
^rq\kf*] 4pT^* if (cpc_tty_area[port].buf_rx.first) {
Psx"[2iZm unsigned char * aux;
sNpA!!\PM while (cpc_tty_area[port].buf_rx.first) {
@u/CNx,`X aux = (unsigned char *)cpc_tty_area[port].buf_rx.first;
/"La@M37 cpc_tty_area[port].buf_rx.first = cpc_tty_area[port].buf_rx.first->next;
qdpi-*2 kfree(aux);
AzJ;EtR }
3^
UoK cpc_tty_area[port].buf_rx.first = NULL;
tTTHQ7o*BD cpc_tty_area[port].buf_rx.last = NULL;
C=&n1/ }
*\'t$se+ z~`X4Segw cpc_tty_area[port].state = CPC_TTY_ST_OPEN;
$6UU58>n cpc_tty_area[port].tty = tty;
"!vY{9, tty->driver_data = &cpc_tty_area[port];
+=9iq3<yfS ;[
Dxk$" cpc_tty_signal_on(cpc_tty->pc300dev, CTL_DTR);
J'ce?_\?PY }
VV1sadS:S` fTR6]i; cpc_tty->num_open++;
aG;F=e %"(HjanH CPC_TTY_DBG("%s: opening TTY driver\n", cpc_tty->name);
cd1-2-4U !YGHJwW: /* avisar driver PC300 */
!Q~>)$Cf^ return 0;
E]n]_{BN] }
Vv(buG T\p>wiY2|F /*
O6?{@l * PC300 TTY CLOSE routine
Q .Nw#r+m *
/# Jvt * This routine is called by the tty driver to close the interface
baG_7>Q9H * o call close channel in PC300 driver (cpc_closech)
a"YVr'| * o free Rx and Tx buffers
zOSUYn */
p: z][I ly34aD/p~, static void cpc_tty_close(struct tty_struct *tty, struct file *flip)
.^=I&X/P {
#'KM$l,P st_cpc_tty_area *cpc_tty;
|(Wwh$ unsigned long flags;
R2~y<^.V`Y int res;
3t+{~{Dj {G vGV if (!tty || !tty->driver_data ) {
/dg?6XT/ CPC_TTY_DBG("hdlx-tty: no TTY in close\n");
J/Y9 X, return;
T5}3Y3G,6 }
-E6av|c,F x*F-d2D cpc_tty = (st_cpc_tty_area *) tty->driver_data;
d m"R0> \,/ozfJ7dT if ((cpc_tty->tty != tty)|| (cpc_tty->state != CPC_TTY_ST_OPEN)) {
yc]_ ?S>9 CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
jEQ_#KKYJ return;
f@ |[pT }
zP0<4E$M` "zNS6I?rzE if (!cpc_tty->num_open) {
.?g=mh79( CPC_TTY_DBG("%s: TTY is closed\n",cpc_tty->name);
"2C}Pr,p8 return;
yw+]S }
ZGH
7_K ec#`9w$ if (--cpc_tty->num_open > 0) {
SbX^DAlB1 CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name);
rz|Sjtq return;
X4:84 }
viU} um$U3'0e cpc_tty_signal_off(cpc_tty->pc300dev, CTL_DTR);
dkEbP*yXg }OcrA/ CPC_TTY_LOCK(cpc_tty->pc300dev->chan->card, flags); /* lock irq */
`UzH *w@e cpc_tty->tty = NULL;
<!G /&T cpc_tty->state = CPC_TTY_ST_INIT;
(T2HUmkQ6 CPC_TTY_UNLOCK(cpc_tty->pc300dev->chan->card, flags); /* unlock irq */
) C~#W 3)>re& if (cpc_tty->buf_rx.first) {
)Rbt0 unsigned char * aux;
@eBo7#Zr while (cpc_tty->buf_rx.first) {
e^~dx}X aux = (unsigned char *)cpc_tty->buf_rx.first;
@qcUxu 4 cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
AFsieJ kfree(aux);
J| &aqY }
T;Kv<G; cpc_tty->buf_rx.first = NULL;
|j'@no_rv cpc_tty->buf_rx.last = NULL;
H&*&n}vh5y }
t OnOzD *wqR .n? kfree(cpc_tty->buf_tx);
8Wtr,%82 cpc_tty->buf_tx = NULL;
+K'YVB
U} ]5*H/8Ke7 CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name);
YSB> WBS-< V+>RF if (!serial_drv.refcount && cpc_tty_unreg_flag) {
0hkYexX73 cpc_tty_unreg_flag = 0;
?\4kV*/Cqz CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
^|h_[> if ((res=tty_unregister_driver(&serial_drv))) {
3VMaD@nYa CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
`r=^{Y cpc_tty->name,res);
:+9. v }
*dB3Gu{
+ }
En-=z`j
G return;
&~.|9P/45 }
dQH8s tD}{/`{_t /*
kd&~_=Q * PC300 TTY WRITE routine
TGG=9a]m *
K(MZ!>{ * This routine is called by the tty driver to write a series of characters
fOSJdX0e|Q * to the tty device. The characters may come from user or kernel space.
h^IizrqU * o verify the DCD signal
+ rN# * o send characters to board and start the transmission
jsV1~1:83 */
m>[G-~0?kI static int cpc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
1GR|$E {
*pS3xit~ st_cpc_tty_area *cpc_tty;
h
}&dvd pc300ch_t *pc300chan;
<uoVGV5N pc300_t *card;
DD7D&@As int ch;
mDwuJf8} unsigned long flags;
]x& R=)P struct net_device_stats *stats;
uW}M1kq?+l 2"
v{ if (!tty || !tty->driver_data ) {
C)qG<PW.! CPC_TTY_DBG("hdlcX-tty: no TTY in write\n");
S9b=?? M) return -ENODEV;
FDBNKQV }
lnMU5[g{ o"N\l{ #s cpc_tty = (st_cpc_tty_area *) tty->driver_data;
!L|VmLqa *6_>/!ywI if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
ZW;Re5?DJ CPC_TTY_DBG("%s: TTY is not opened\n", cpc_tty->name);
XI ><;# return -ENODEV;
.Q</0*sp }
W1M Bk[:Q _iqaKYT$ if (count > CPC_TTY_MAX_MTU) {
N1:)Z`r CPC_TTY_DBG("%s: count is invalid\n",cpc_tty->name);
tnb'\}Vn return -EINVAL; /* frame too big */
8&x&Ou$("V }
2I=4l .ArOZ{lKD> CPC_TTY_DBG("%s: cpc_tty_write data len=%i\n",cpc_tty->name,count);
Ho%%voJBS !YM:?%B pc300chan = (pc300ch_t *)((pc300dev_t*)cpc_tty->pc300dev)->chan;
2B6y1" B stats = &cpc_tty->pc300dev->dev->stats;
{Aj=Rj@ card = (pc300_t *) pc300chan->card;
X"f] ch = pc300chan->channel;
GIkVU6Q} nGJ+.z /* verify DCD signal*/
'OhGSs| if (cpc_readb(card->hw.scabase + M_REG(ST3,ch)) & ST3_DCD) {
>^@~}]L /* DCD is OFF */
l~1l~Gx_&n CPC_TTY_DBG("%s : DCD is OFF\n", cpc_tty->name);
Fv^>^txh stats->tx_errors++;
L[+4/a!HQ stats->tx_carrier_errors++;
+OInf_O CPC_TTY_LOCK(card, flags);
mX@xV*
cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR);
HXB&
6 j3?@p5E( if (card->hw.type == PC300_TE) {
/5>A 2y cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
VB+_ kR6Zv cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) &
~^3U@(: ~(CPLD_REG2_FALC_LED1 << (2 *ch)));
A(C0/|#V }
H7 {kl o8A(Cg} CPC_TTY_UNLOCK(card, flags);
%NhZTmWm @C~gU@F return -EINVAL;
-?)z@Lc }
QcdAg%"yy Jd|E
4h~( if (cpc_tty_send_to_card(cpc_tty->pc300dev, (void*)buf, count)) {
=@;\9j /* failed to send */
5G#2#Al(F
CPC_TTY_DBG("%s: trasmition error\n", cpc_tty->name);
%x^ U3"7 return 0;
A22'qgKm@ }
B1U7z1< return count;
=#I/x=L: }
nG|
NRp Q,o"[ &Gp /*
U]E~7C * PC300 TTY Write Room routine
^{O1+7d[. *
Hq <!& * This routine returns the numbers of characteres the tty driver will accept
\-Q6z8 * for queuing to be written.
R{Me~L? * o return MTU
?[X^'zz} */
Jj+Hj[(@ static int cpc_tty_write_room(struct tty_struct *tty)
|s !7U {
n--s[Kdo8 st_cpc_tty_area *cpc_tty;
OJ#
d ~N+H7T.L if (!tty || !tty->driver_data ) {
A9y3B^\* CPC_TTY_DBG("hdlcX-tty: no TTY to write room\n");
Kl :x?"g) return -ENODEV;
a7fn{VU8 }
eC$ Jdf Yc>.P cpc_tty = (st_cpc_tty_area *) tty->driver_data;
MF5o\-&dN M+M\3U if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
&UX:KW`= CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
@aWd0e] return -ENODEV;
Dgz^s^fxU }
E*UE?4FSw| H')8p;~{} CPC_TTY_DBG("%s: write room\n",cpc_tty->name);
x?G"58 -h&KC{Xab return CPC_TTY_MAX_MTU;
CGZ3-OW@E }
|#O>DdKHT zhC5%R &n/ /*
Wtj*Z.=: * PC300 TTY chars in buffer routine
\hqjk:o *
`mDCX * This routine returns the chars number in the transmission buffer
s>e)\9c * o returns 0
lD1m<AC */
qL!pDZk static int cpc_tty_chars_in_buffer(struct tty_struct *tty)
Nb/Z + {
z CFXQi st_cpc_tty_area *cpc_tty;
{bO
O?pp GsNZr=;C if (!tty || !tty->driver_data ) {
jtQ} CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n");
,\ zx4* return -ENODEV;
43BqNQ0 }
E Ks4N4k LVBE+{P\5? cpc_tty = (st_cpc_tty_area *) tty->driver_data;
pn
aSOyR F+m;y if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
;0:[X+"( CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
X32{y973hT return -ENODEV;
63pd W/\j }
!,cfA';S l[b`4 return 0;
Dq9*il;' }
Kr@6m80E5 7) Qq static int pc300_tiocmset(struct tty_struct *tty,
' )KuLVE}S unsigned int set, unsigned int clear)
~y8KQ-1n" {
?!$:I8T st_cpc_tty_area *cpc_tty;
t,308Z [ih^VlZ CPC_TTY_DBG("%s: set:%x clear:%x\n", __func__, set, clear);
lWk/vj<5 R
b=q
# if (!tty || !tty->driver_data ) {
+;N;r/d_i CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n");
'Em633 return -ENODEV;
_+}#
}
gH|:=vfYUR C?t!Uvs cpc_tty = (st_cpc_tty_area *) tty->driver_data;
L }pj+xB &[y+WrGG if (set & TIOCM_RTS)
XW q@47FR cpc_tty_signal_on(cpc_tty->pc300dev, CTL_RTS);
4~z-&>% if (set & TIOCM_DTR)
b_a6| cpc_tty_signal_on(cpc_tty->pc300dev, CTL_DTR);
4*V[^mht JO&L1<B{v if (clear & TIOCM_RTS)
a<lDT_2b cpc_tty_signal_off(cpc_tty->pc300dev, CTL_RTS);
-$cO0RSY if (clear & TIOCM_DTR)
O{ |Ug~ cpc_tty_signal_off(cpc_tty->pc300dev, CTL_DTR);
Oc%W_Gb7 oR'u&\mB return 0;
Nd%j0lj }
E5g|*M.+f QEc4l[^{.B static int pc300_tiocmget(struct tty_struct *tty)
yUEvva {
X
v$"B-j unsigned int result;
-nDY3$U/ unsigned char status;
Pd;G c@'~ unsigned long flags;
K aNO&%qX st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *) tty->driver_data;
A/ 88WC$v pc300dev_t *pc300dev = cpc_tty->pc300dev;
1}3tpO; pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan;
WlmkM?@ pc300_t *card = (pc300_t *) pc300chan->card;
9i+`,r
int ch = pc300chan->channel;
;={3H_{3 W7"UhM cpc_tty = (st_cpc_tty_area *) tty->driver_data;
C!W0L`r N}KL' CPC_TTY_DBG("%s-tty: tiocmget\n",
U}DLzn|w ((struct net_device*)(pc300dev->hdlc))->name);
)F,z pGG 'C)
v?!19 CPC_TTY_LOCK(card, flags);
%'.3t|zH status = cpc_readb(card->hw.scabase+M_REG(CTL,ch));
CO`?M,x> CPC_TTY_UNLOCK(card,flags);
I*H($ a t
{H{xd result = ((status & CTL_DTR) ? TIOCM_DTR : 0) |
CI^s~M > ((status & CTL_RTS) ? TIOCM_RTS : 0);
1G)I|v9R zV8{|-2]No return result;
2BV]@]qB }
+P%k@w#<Z #|=Q5"wU /*
.Ky)Co * PC300 TTY Flush Buffer routine
"w3%BbI x *
moL3GV%]Gq * This routine resets the transmission buffer
cc 0Tb */
L5r02VzbD static void cpc_tty_flush_buffer(struct tty_struct *tty)
6o4Y]C2W{1 {
0G`@^` st_cpc_tty_area *cpc_tty;
HYl~)O> st)qw]Dn;Y if (!tty || !tty->driver_data ) {
!wTrWD! CPC_TTY_DBG("hdlcX-tty: no TTY to flush buffer\n");
]Hg6Mz>Mj return;
8^sh@j2L }
P|t2%:_ B_
bZa cpc_tty = (st_cpc_tty_area *) tty->driver_data;
5&qBG@Hw] Z?u}?-b1\H if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
^G4@cR.An CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
{@hJPK8 return;
Uo;a$sR }
b>Ea_3T/ Hb0_QT~ CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name);
/QT>" 3Uej]}c tty_wakeup(tty);
k~)@D| ? return;
nf1O8FwRb }
G|u)eW tXcZl!3x /*
~BMUea( * PC300 TTY Hangup routine
TjHt:%7. *
l+j
!CvtI * This routine is called by the tty driver to hangup the interface
6sG5n7E-A * o clear DTR signal
w)&?9?~ */
#4<=Ira5 \>x1#Vr>#V static void cpc_tty_hangup(struct tty_struct *tty)
KW$.Yy {
FmSE]et st_cpc_tty_area *cpc_tty;
z_Hkw3? int res;
e$/y~! (I/iD.A if (!tty || !tty->driver_data ) {
A]ZQ?-L/ CPC_TTY_DBG("hdlcX-tty: no TTY to hangup\n");
~t n$AtK return ;
%xr'96d }
'x5p ?m o`G6! cpc_tty = (st_cpc_tty_area *) tty->driver_data;
,&y_^-|d &2S-scP if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
H3 -?cy CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
xT(0-o* return ;
+\$c_9|C+ }
z[6avW"q if (!serial_drv.refcount && cpc_tty_unreg_flag) {
"!CVm{7[ cpc_tty_unreg_flag = 0;
&XCP@@T CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
[5ncBY*A7 if ((res=tty_unregister_driver(&serial_drv))) {
n.t5:SW CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
Mdq|:^px cpc_tty->name,res);
#<X4RJ }
RA?_j$ }
6TTu[*0NT cpc_tty_signal_off(cpc_tty->pc300dev, CTL_DTR);
:{4C2qK> }
mE_% :,fT^izew /*
=CO) Q2 * PC300 TTY RX work routine
Bh'!aip k * This routine treats RX work
]=9 d'WL * o verify read buffer
ay|jq"a * o call the line disc. read
!&:Cp_ * o free memory
rIb~@cR) */
@,7r<6E static void cpc_tty_rx_work(struct work_struct *work)
/nsBUM[; {
![$`Ivro` st_cpc_tty_area *cpc_tty;
%8wBZ~1- unsigned long port;
dm]g:KWg int i, j;
Hzj8o3 volatile st_cpc_rx_buf *buf;
`e fiX^ char flags=0,flg_rx=1;
p(nO~I2E struct tty_ldisc *ld;
#d*0
)w -2!S>P Zs if (cpc_tty_cnt == 0) return;
Z=Cw7E L>mM6$l for (i=0; (i < 4) && flg_rx ; i++) {
-agB ]j flg_rx = 0;
1zCu1'Wv 'n>44_7 L cpc_tty = container_of(work, st_cpc_tty_area, tty_rx_work);
,|A6l?iV port = cpc_tty - cpc_tty_area;
o.w/? 8#g}ev@|u for (j=0; j < CPC_TTY_NPORTS; j++) {
vq.o;q / cpc_tty = &cpc_tty_area[port];
lJN#_V0qW k=mLcP if ((buf=cpc_tty->buf_rx.first) != NULL) {
! L|l(<C if (cpc_tty->tty) {
/W`CqJk-*. ld = tty_ldisc_ref(cpc_tty->tty);
,X1M!' if (ld) {
U;TS7A3 if (ld->ops->receive_buf) {
kZo#Ny CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
w=<E) ld->ops->receive_buf(cpc_tty->tty, (char *)(buf->data), &flags, buf->size);
UJQTArf }
[s`B0V`04 tty_ldisc_deref(ld);
,A7:zxnc.V }
yz!L:1DG }
EpKZ.lCU cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
%rJDpB{ kfree((void *)buf);
$]Ix(7@W buf = cpc_tty->buf_rx.first;
8_w6% md flg_rx = 1;
0Ze&GK'Hf }
_>]/. w2= if (++port == CPC_TTY_NPORTS) port = 0;
)Ute }
B9y5NX }
EC0B6!C&7 }
Y:\]d1C g!'
x5#]n /*
{5D%<Te * PC300 TTY RX work routine
DBHHJD/q *
0^Vw^]w * This routine treats RX interrupt.
SP<Sv8Okj * o read all frames in card
!vRN'/(Vyu * o verify the frame size
7Hv6>z#m * o read the frame in rx buffer
$xdo=4;| */
Q]GS#n static void cpc_tty_rx_disc_frame(pc300ch_t *pc300chan)
1%vE 7a>{ {
t(V2 volatile pcsca_bd_t __iomem * ptdescr;
{fDRVnI? volatile unsigned char status;
+X+R8 pc300_t *card = (pc300_t *)pc300chan->card;
H)E,([ int ch = pc300chan->channel;
{d'B._#i "%+||IyW /* dma buf read */
1 oKY7i$ ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase +
"~EAt$ RX_BD_ADDR(ch, pc300chan->rx_first_bd));
Sin)]zG~0 while (pc300chan->rx_first_bd != pc300chan->rx_last_bd) {
0sKoNzE status = cpc_readb(&ptdescr->status);
Q?LzL(OioN cpc_writeb(&ptdescr->status, 0);
k$m'ebrS.~ cpc_writeb(&ptdescr->len, 0);
9z{}DBA pc300chan->rx_first_bd = (pc300chan->rx_first_bd + 1) &
Lg,ObVt! (N_DMA_RX_BUF - 1);
eN|zD?ba& if (status & DST_EOM) {
B8T5?bl break; /* end of message */
qGR1$\] }
8YE4ln ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase + cpc_readl(&ptdescr->next));
Fje
/;p }
Q.7X3A8 }
~N;
dX[@BT fV7
k {dR void cpc_tty_receive(pc300dev_t *pc300dev)
!Sy9v {
"QS(4yw?jg st_cpc_tty_area *cpc_tty;
+}(]7du pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan;
G7,v:dlK pc300_t *card = (pc300_t *)pc300chan->card;
uEr.LCAS int ch = pc300chan->channel;
.Jg<H %%f volatile pcsca_bd_t __iomem * ptdescr;
s/~pr.>-l struct net_device_stats *stats = &pc300dev->dev->stats;
<3tf(?*,k] int rx_len, rx_aux;
ow
6\j:$? volatile unsigned char status;
z;@<J8I unsigned short first_bd = pc300chan->rx_first_bd;
+/%4E % st_cpc_rx_buf *new = NULL;
7^Us unsigned char dsr_rx;
s"nntC Sh-B! if (pc300dev->cpc_tty == NULL) {
n%;t Va return;
GmJ
\3]{PZ }
#')]~Xa ;sf'"UnL dsr_rx = cpc_readb(card->hw.scabase + DSR_RX(ch));
!=;Evf A>F&b1 cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty;
yGWl8\,j0 7KRNTnd while (1) {
xDekC~Zq rx_len = 0;
H3d|eO4+W ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd));
#z.\pd while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
K^GvU 0\ rx_len += cpc_readw(&ptdescr->len);
Gv 8Z first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1);
\!uf*=d if (status & DST_EOM) {
{ i3x\| break;
*"F*6+}w" }
Qd% (]L[N. ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase+cpc_readl(&ptdescr->next));
TQ/# }
X,o ]tgg= d@cyQFX if (!rx_len) {
"Ya;&F.' if (dsr_rx & DSR_BOF) {
em^2\*sxpA /* update EDA */
s%>u[-9U cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch),
r[kHVT8 RX_BD_ADDR(ch, pc300chan->rx_last_bd));
Sqmjf@o$> }
hN1[*cF kfree(new);
o0b\<} return;
ci(BPnQ }
l{ fL~O
b^8"EBo if (rx_len > CPC_TTY_MAX_MTU) {
ao2o!-?!t /* Free RX descriptors */
aOoWB^;6 CPC_TTY_DBG("%s: frame size is invalid.\n",cpc_tty->name);
biffBC:q stats->rx_errors++;
}!s$
/Kn stats->rx_frame_errors++;
Z,XivU& cpc_tty_rx_disc_frame(pc300chan);
ov!L8
9`[u continue;
+{)V%"{u: }
Wk-.dJ _A]~`/0;` new = kmalloc(rx_len + sizeof(st_cpc_rx_buf), GFP_ATOMIC);
=5%}CbUU)4 if (!new) {
&|zV Wl cpc_tty_rx_disc_frame(pc300chan);
l$!NEOK continue;
*;t_VlaZ }
!a5e{QG0 RhjU^,% /* dma buf read */
j=>WWlZ ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase +
`wLmGv+V RX_BD_ADDR(ch, pc300chan->rx_first_bd));
=E~SaT ^'sOWIzeiY rx_len = 0; /* counter frame size */
SnO,-Rg !$o9:[B while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
,Qe`(vU*s rx_aux = cpc_readw(&ptdescr->len);
tE=$# if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT))
A#79$[>w || (rx_aux > BD_DEF_LEN)) {
WPu%{/[ CPC_TTY_DBG("%s: reception error\n", cpc_tty->name);
E@="n<uS stats->rx_errors++;
ooYs0/,{ if (status & DST_OVR) {
oX/#Mct{s stats->rx_fifo_errors++;
#:
,X^"w3 }
,(#n8|q4 if (status & DST_CRC) {
d
k|X&)xTJ stats->rx_crc_errors++;
5hiuBf< }
jLul:*
L if ((status & (DST_RBIT | DST_SHRT | DST_ABT)) ||
o`ODz[04 (rx_aux > BD_DEF_LEN)) {
z]i/hU stats->rx_frame_errors++;
LA837%) }
90$`AMR /* discard remainig descriptors used by the bad frame */
r3Ih]|FK# CPC_TTY_DBG("%s: reception error - discard descriptors",
;wr]_@<~ cpc_tty->name);
+G!;:o cpc_tty_rx_disc_frame(pc300chan);
8ax3"G rx_len = 0;
:OY7y`hRG kfree(new);
sE(mK<{pk new = NULL;
FCYZ9L5uF break; /* read next frame - while(1) */
hN:2(x }
UDa\* TUO#6 if (cpc_tty->state != CPC_TTY_ST_OPEN) {
=Sn!'@%U] /* Free RX descriptors */
v#KE"m cpc_tty_rx_disc_frame(pc300chan);
Aa%ks+1 stats->rx_dropped++;
Bk1gE(( rx_len = 0;
C?b_E kfree(new);
zB{be_Tw new = NULL;
{D&:^f break; /* read next frame - while(1) */
4\8k~# }
=CO#Q$ ~48mCD /* read the segment of the frame */
*
@j#13. if (rx_aux != 0) {
}EHmVPe memcpy_fromio((new->data + rx_len),
*W<g%j-a (void __iomem *)(card->hw.rambase +
.J|"bs9 cpc_readl(&ptdescr->ptbuf)), rx_aux);
}Rq-IRa' rx_len += rx_aux;
.gHL(*1P }
Ibl==Irk cpc_writeb(&ptdescr->status,0);
5 %aT cpc_writeb(&ptdescr->len, 0);
W?auY_+P pc300chan->rx_first_bd = (pc300chan->rx_first_bd + 1) &
YJrZ (N_DMA_RX_BUF -1);
"PPn^{bYm if (status & DST_EOM)break;
C0Ti9 uH!;4@uI ptdescr = (pcsca_bd_t __iomem *) (card->hw.rambase +
ma26|N5 cpc_readl(&ptdescr->next));
y3C$%yv0 }
LaiUf_W #X /* update pointer */
>@d=\Kyu pc300chan->rx_last_bd = (pc300chan->rx_first_bd - 1) &
E%+1^
L (N_DMA_RX_BUF - 1) ;
4;d9bd)A if (!(dsr_rx & DSR_BOF)) {
1Q$Z'E}SK@ /* update EDA */
b$ )XS cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch),
^?tF'l` RX_BD_ADDR(ch, pc300chan->rx_last_bd));
kQm\;[R }
pfvNVu if (rx_len != 0) {
Tp/+{|~ stats->rx_bytes += rx_len;
$
V"7UA22 "ealYveu if (pc300dev->trace_on) {
i'9 cpc_tty_trace(pc300dev, new->data,rx_len, 'R');
]"M 4fA }
%.D@{O new->size = rx_len;
.Su9fjy% new->next = NULL;
Iih~rWJ if (cpc_tty->buf_rx.first == NULL) {
&wZ:$lK#o cpc_tty->buf_rx.first = new;
SNd]c cpc_tty->buf_rx.last = new;
GVp2|\-L } else {
dKyX70Zy9 cpc_tty->buf_rx.last->next = new;
aOj5b>> cpc_tty->buf_rx.last = new;
qN_jsJ }
z|SLH<~ schedule_work(&(cpc_tty->tty_rx_work));
PRCr7f stats->rx_packets++;
ghiFI<)VY }
q-}J0vu\K }
2FS,B\d }
S<LHNZu|^A N*My2t_+E /*
|nj%G< * PC300 TTY TX work routine
Piz/vH6M} *
T{j&w% (z * This routine treats TX interrupt.
iffRGnN^e * o if need call line discipline wakeup
~>ACMO * o call wake_up_interruptible
P Z;O
pp */
I=pTfkTT static void cpc_tty_tx_work(struct work_struct *work)
-U=bC {
h7
> st_cpc_tty_area *cpc_tty =
0BIH.ZV# container_of(work, st_cpc_tty_area, tty_tx_work);
]baO{pJi struct tty_struct *tty;
` oYrW0Vm W 6~B~L CPC_TTY_DBG("%s: cpc_tty_tx_work init\n",cpc_tty->name);
6?ylSQ]1 pUr.<yc&u if ((tty = cpc_tty->tty) == NULL) {
avk0pY(n CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name);
hun/H4f| return;
Y]nY.5irL }
o$YL\ <qp tty_wakeup(tty);
uV *&a~ }
O&irgc! g(MeCoCc /*
?!~CX`eMZ * PC300 TTY send to card routine
ek#{!9- *
Ut8yA"Y~ * This routine send data to card.
t*^Q`V wQ * o clear descriptors
vh+IhGi * o write data to DMA buffers
[*#ms=Zdc * o start the transmission
[:sV;37s */
0AFjO) static int cpc_tty_send_to_card(pc300dev_t *dev,void* buf, int len)
BfIGw {
K9gfS V>] pc300ch_t *chan = (pc300ch_t *)dev->chan;
qI"@ PI!s pc300_t *card = (pc300_t *)chan->card;
q?{wRBVVB int ch = chan->channel;
a!P?RbW struct net_device_stats *stats = &dev->dev->stats;
Cjsy1gA
unsigned long flags;
+dk}$w[g volatile pcsca_bd_t __iomem *ptdescr;
a4L8MgF&$- int i, nchar;
FU~ Ip int tosend = len;
]7-*1kL8=~ int nbuf = ((len - 1)/BD_DEF_LEN) + 1;
$AUC#<*C unsigned char *pdata=buf;
o6ec\v!l- 4lE
j/#} CPC_TTY_DBG("%s:cpc_tty_send_to_cars len=%i",
BYrj#n5 (st_cpc_tty_area *)dev->cpc_tty->name,len);
WeE>4>^ *<zfe. if (nbuf >= card->chan[ch].nfree_tx_bd) {
soXeHjNl return 1;
y'pAhdF }
p0UR5A>p ?x"<0k1g /* write buffer to DMA buffers */
*obBo6!zM CPC_TTY_DBG("%s: call dma_buf_write\n",
5EIh5Y EU> (st_cpc_tty_area *)dev->cpc_tty->name);
vz*QzVk1 for (i = 0 ; i < nbuf ; i++) {
@]t} bF] ptdescr = (pcsca_bd_t __iomem *)(card->hw.rambase +
)&<BQIv9/ TX_BD_ADDR(ch, card->chan[ch].tx_next_bd));
JVFn=Mw nchar = (BD_DEF_LEN > tosend) ? tosend : BD_DEF_LEN;
m49GCo k+ if (cpc_readb(&ptdescr->status) & DST_OSB) {
noC]&4b memcpy_toio((void __iomem *)(card->hw.rambase +
Y'`w.+9 cpc_readl(&ptdescr->ptbuf)),
U`D/~KJ{Y &pdata[len - tosend],
Tz]t.]!&E nchar);
_K3?0<=4 card->chan[ch].nfree_tx_bd--;
ogH{ if ((i + 1) == nbuf) {
AF>J8 V /* This must be the last BD to be used */
tpO%)* cpc_writeb(&ptdescr->status, DST_EOM);
OW\r } } else {
V.6h6B!vB cpc_writeb(&ptdescr->status, 0);
B)O{+avu }
|vw0:\/H cpc_writew(&ptdescr->len, nchar);
%,E\8{I+
} else {
>fPa>[_1 CPC_TTY_DBG("%s: error in dma_buf_write\n",
iVLfAN @ (st_cpc_tty_area *)dev->cpc_tty->name);
N2vSJ\u stats->tx_dropped++;
F?? })YX return 1;
<<W{nSm# }
]
hGU.C"( tosend -= nchar;
$+!/=8R) card->chan[ch].tx_next_bd =
!jGe_xB}~ (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1);
-uXf?sTV }
V6B`q;lA UmcPpZ if (dev->trace_on) {
Ds?
@LE| cpc_tty_trace(dev, buf, len,'T');
fgK1+sW }
qR/~a K>hQls+ /* start transmission */
lM~ 3yBy CPC_TTY_DBG("%s: start transmission\n",
c"Ddw'?e (st_cpc_tty_area *)dev->cpc_tty->name);
gE]6]L *]NG@^y CPC_TTY_LOCK(card, flags);
NKd}g cpc_writeb(card->hw.scabase + DTX_REG(EDAL, ch),
E*'sk TX_BD_ADDR(ch, chan->tx_next_bd));
xr}3vJ7 cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_ENA);
O%L]*vIr cpc_writeb(card->hw.scabase + DSR_TX(ch), DSR_DE);
}lt5!u~} "`qmeZ$rg if (card->hw.type == PC300_TE) {
T^> ST cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
Zvz Zs cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) |
SEa'>UG (CPLD_REG2_FALC_LED1 << (2 * ch)));
Fcz7 }
7ESSx"^B CPC_TTY_UNLOCK(card, flags);
o{7wPwQ;* return 0;
mQdF+b1o }
S-l<+O1fy ^)oBa=jL4 /*
'c7C*6;a * PC300 TTY trace routine
=jXBF. *
4zyN>f| * This routine send trace of connection to application.
vqO d`_) * o clear descriptors
LK\L}<;1V * o write data to DMA buffers
M#%l} * o start the transmission
3m%oXT */
4eH:eCZze g27 iE static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx)
]!CMo+ {
oGt,^!V1 struct sk_buff *skb;
6G.(o 'EzKu~* if ((skb = dev_alloc_skb(10 + len)) == NULL) {
9U~fc U6 /* out of memory */
!\|_,pSB CPC_TTY_DBG("%s: tty_trace - out of memory\n", dev->dev->name);
#&0G$~ return;
|H-%F?<{ }
|i_+b@Lul _L?MYkD skb_put (skb, 10 + len);
j.=&qYc0" skb->dev = dev->dev;
>7g #e,d skb->protocol = htons(ETH_P_CUST);
e}l F#$ skb_reset_mac_header(skb);
pmda9V4 skb->pkt_type = PACKET_HOST;
\LuaI skb->len = 10 + len;
gOiZ8K! O-P'Ff"}t skb_copy_to_linear_data(skb, dev->dev->name, 5);
4eVQO%&2 skb->data[5] = '[';
nA owFdCD skb->data[6] = rxtx;
qzon);#7w skb->data[7] = ']';
0?V{u`* skb->data[8] = ':';
m\zCHX#n skb->data[9] = ' ';
D6+^Qmu"p skb_copy_to_linear_data_offset(skb, 10, buf, len);
3CL1Z\8To netif_rx(skb);
DVJuX~'|! }
TAL,(&[s L%S(z)xX3 /*
W%ml/ 4 * PC300 TTY unregister service routine
M./1.k&@ *
4,g_$) * This routine unregister one interface.
_2Zc?*4 */
|P_voht void cpc_tty_unregister_service(pc300dev_t *pc300dev)
>]{{5oOQ> {
r+=%Ag st_cpc_tty_area *cpc_tty;
?)NgODU ulong flags;
zv.#9^/y int res;
&=f] a J497
>w[ if ((cpc_tty= (st_cpc_tty_area *) pc300dev->cpc_tty) == NULL) {
B:)PUBb CPC_TTY_DBG("%s: interface is not TTY\n", pc300dev->dev->name);
SfSWjq return;
W3+;1S$k }
d+qeZGg^A CPC_TTY_DBG("%s: cpc_tty_unregister_service", cpc_tty->name);
^O0trM>h- C6"{-{H if (cpc_tty->pc300dev != pc300dev) {
z`H|]${X CPC_TTY_DBG("%s: invalid tty ptr=%s\n",
vzX%x ul pc300dev->dev->name, cpc_tty->name);
]ZR}Pm/CA
return;
8Y"R@'~ }
>//yvkZ9, Cl6P,C if (--cpc_tty_cnt == 0) {
"tT68 if (serial_drv.refcount) {
w`GjQIA CPC_TTY_DBG("%s: unregister is not possible, refcount=%d",
:(A k: cpc_tty->name, serial_drv.refcount);
_<Ip0?N cpc_tty_cnt++;
_|r/*(hh cpc_tty_unreg_flag = 1;
ajCe&+ return;
x)R1aq } else {
Prc( CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
'QSj- if ((res=tty_unregister_driver(&serial_drv))) {
~@#s<a,%; CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
GX+Gqj. cpc_tty->name,res);
V=gu'~ }
g& ou[_A }
i);BTwW)#] }
3mQ3mV: CPC_TTY_LOCK(pc300dev->chan->card,flags);
|F4)&xN\ cpc_tty->tty = NULL;
g'+2bQ CPC_TTY_UNLOCK(pc300dev->chan->card, flags);
XE'3p6 cpc_tty->tty_minor = 0;
s
.@S zq cpc_tty->state = CPC_TTY_ST_IDLE;
`*o ko[\3 }
/H?) qk |Ed?s /*
U:AB%gr[ * PC300 TTY trigger poll routine
5d;(D i5z * This routine is called by pc300driver to treats Tx interrupt.
v4]#Nc$~T */
],
IQ~ void cpc_tty_trigger_poll(pc300dev_t *pc300dev)
CZ!gu Y= {
a|5<L st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty;
00LL&ot if (!cpc_tty) {
mGpBj9jr1 return;
zh{I;~syh }
lDL(,ZZS` schedule_work(&(cpc_tty->tty_tx_work));
x"7PnN|~ }
6<%b}q9Mo Z,-J
tl /*
#Vhr1;j * PC300 TTY reset var routine
$azK M,<q * This routine is called by pc300driver to init the TTY area.
vA{DF{S4 */
QFB2,k6jN } rX)A\ g6 void cpc_tty_reset_var(void)
0h
kZ {
\j<aFOT( int i ;
bw)E;1zo D;h JK-Y CPC_TTY_DBG("hdlcX-tty: reset variables\n");
_H@8qR /* reset the tty_driver structure - serial_drv */
r]'[qaP memset(&serial_drv, 0, sizeof(struct tty_driver));
RA!8AS? for (i=0; i < CPC_TTY_NPORTS; i++){
x6Tpt^N} memset(&cpc_tty_area
,0, sizeof(st_cpc_tty_area)); g,G{%dGsk
} fo=@ X>S
} uF ;8B]"