Commit | Line | Data |
---|---|---|
398dc04d MK |
1 | /* |
2 | * Datakit driver | |
3 | * SCCSID[] = "@(#)dk.c 2.1 DKHOST 84/07/03" | |
4 | */ | |
5 | ||
6 | #include "datakit.h" | |
7 | #if NDATAKIT>0 | |
8 | ||
b28b3a13 KB |
9 | #include "../include/pte.h" |
10 | #include "sys/param.h" | |
11 | #include "sys/signal.h" | |
12 | #include "sys/errno.h" | |
13 | #include "sys/conf.h" | |
14 | #include "sys/user.h" | |
15 | #include "sys/ioctl.h" | |
16 | #include "sys/tty.h" | |
17 | #include "sys/vnode.h" | |
18 | #include "sys/file.h" | |
19 | #include "sys/systm.h" | |
20 | #include "sys/proc.h" | |
21 | #include "sys/mbuf.h" | |
22 | #include "sys/buf.h" | |
23 | #include "sys/uio.h" | |
24 | #include "sys/kernel.h" | |
25 | #include "sys/dkit.h" | |
26 | #include "sys/dkcmc.h" | |
27 | #include "sys/dk.h" | |
28 | #include "sys/dkdev.h" | |
29 | #include "sys/syslog.h" | |
398dc04d MK |
30 | |
31 | extern int dkdebug ; | |
32 | ||
33 | #define DKBUFUSE 5 /* max buffers /channel */ | |
34 | ||
35 | ||
36 | int dk_nchan = NDATAKIT; | |
37 | struct dkdev dkdev[NDATAKIT]; | |
38 | struct dksetupreq *dkreq[NDATAKIT]; | |
39 | ||
40 | ||
41 | ||
42 | ||
43 | #ifdef MONITOR | |
44 | int dummy ; | |
45 | int *DKP2 = &dummy ; | |
46 | #define M_ON(a) *DKP2 |= (a) | |
47 | #define M_OFF(a) *DKP2 &= ~(a) | |
48 | ||
49 | #define Mread 0400 | |
50 | #define Mrslp 01000 | |
51 | #define Mrcpy 02000 | |
52 | #define Mwrite 04000 | |
53 | #define Mwcpy 010000 | |
54 | ||
55 | #else | |
56 | #define M_ON(a) | |
57 | #define M_OFF(a) | |
58 | #endif | |
59 | ||
60 | ||
61 | ||
62 | /*ARGSUSED*/ | |
63 | dkioctl(dev, cmd, data, flag) | |
64 | register caddr_t data; | |
65 | { | |
66 | register struct dkdev *tp; | |
67 | register chanstat ; | |
68 | int chan, sp_chan; | |
1059378b | 69 | int s, error = 0; |
398dc04d MK |
70 | register short *pp ; |
71 | struct dkdev *tsp; | |
72 | extern dkidone() ; | |
73 | struct diocdial dialreq; | |
74 | extern int commchan; | |
75 | ||
76 | chan = dev = minor(dev); | |
77 | tp = &dkdev[chan]; | |
78 | pp = (short *) data; | |
79 | switch(cmd) { | |
80 | case DIOCEXCL: | |
81 | tp->d_state |= DKXCLUDE ; | |
82 | break ; | |
83 | case DIOCNXCL: | |
84 | tp->d_state &= ~DKXCLUDE ; | |
85 | break ; | |
86 | case DIOCSETK: | |
87 | dkdebug = pp[0] ; | |
88 | break; | |
89 | case DIOCQQABO: | |
90 | pp[0] = tp->d_rresid; | |
91 | pp[1] = tp->d_rdone; | |
92 | pp[2] = tp->d_rctl; | |
93 | break ; | |
94 | case DIOCRMODE: | |
95 | if (pp[0] & DKR_TIME) | |
96 | tp->d_rmode = (DKR_TIME | DKR_BLOCK); | |
97 | else tp->d_rmode = pp[0] ; | |
98 | break ; | |
99 | case DIOCXCTL: | |
100 | tp->d_xctl = pp[0] ; | |
101 | break ; | |
102 | case DIOCFLUSH: | |
103 | dk_cmd(chan, DKC_XINIT|DKC_FLUSH); | |
104 | break; | |
105 | case KIOCINIT: | |
106 | dk_cmd(chan, DKC_XINIT); | |
107 | break; | |
108 | case DIOCXWIN: | |
109 | return dk_winsize(chan, (struct diocxwin *)data); | |
110 | case DIOCRESET: | |
111 | if (chan != 1 && chan != pp[0]) return EACCES; | |
112 | if (pp[0] > 1 && pp[0] < commchan) return EINVAL; | |
113 | if (pp[0] < 0 || pp[0] >= dk_nchan) return EINVAL; | |
114 | if (pp[0] == 0) return -dk_close(0); | |
115 | else dk_reset(pp[0]); | |
116 | break; | |
117 | case DIOCCTYPE: | |
118 | if (tp->d_ctype == NULL) { | |
119 | struct mbuf *m; | |
120 | ||
121 | MGET(m, M_WAIT, DKMT_CTYPE); | |
122 | if (m == NULL) | |
123 | return ENOBUFS; | |
124 | tp->d_ctype = mtod(m, struct diocctype *); | |
125 | } | |
126 | return bcopy(data, (caddr_t) tp->d_ctype, sizeof (struct diocctype)); | |
127 | case DIOCINFO: | |
128 | ((struct diocinfo *)data)->dioc_nchan = dk_nchan; | |
129 | ((struct diocinfo *)data)->dioc_channum = chan; | |
130 | ((struct diocinfo *)data)->dioc_commchan = commchan; | |
131 | break; | |
132 | case DIOCSTAT: | |
133 | if (*((int *)data) < 0 || *((int *)data) >= dk_nchan) | |
134 | return EINVAL; | |
135 | *((int *)data) = dk_status(*((int *)data)); | |
136 | break; | |
137 | case FIONBIO: | |
138 | if (*(int *)data) | |
139 | tp->dc_state |= DK_NDELAY; | |
140 | else | |
141 | tp->dc_state &= ~DK_NDELAY; | |
142 | break; | |
143 | case FIOASYNC: | |
144 | if (*(int *)data) | |
145 | tp->dc_state |= DK_ASYNC; | |
146 | else | |
147 | tp->dc_state &= ~DK_ASYNC; | |
148 | break; | |
149 | case TIOCGPGRP: | |
150 | *(int *)data = tp->d_pgrp; | |
151 | break; | |
152 | case TIOCSPGRP: | |
153 | tp->d_pgrp = *(int *)data; | |
154 | break; | |
155 | ||
156 | /* splice chan to file descriptor */ | |
157 | case DKIOCSPL: | |
1059378b | 158 | error = copyin(*(caddr_t *)data, (caddr_t) tp->d_param, |
398dc04d | 159 | 3*sizeof (short)); |
1059378b KM |
160 | if (error) return error; |
161 | if ((error = dkgetdev(tp->d_param[0], &sp_chan)) <= 0) | |
162 | return error ; | |
398dc04d MK |
163 | if (sp_chan == chan) |
164 | return EINVAL ; | |
165 | tsp = &dkdev[sp_chan] ; | |
166 | tp->dc_state |= DKSETUP ; | |
167 | tsp->dc_state |= DKSETUP ; | |
168 | if (dk_splice(chan, sp_chan, dkidone, (caddr_t) tp, | |
169 | (caddr_t) tsp)) { | |
170 | tp->dc_state &= ~DKSETUP ; | |
171 | tsp->dc_state &= ~DKSETUP ; | |
172 | return EIO ; | |
173 | } | |
174 | s = spl5() ; | |
e9036de0 MK |
175 | error = 0; |
176 | while (error == 0 && tp->dc_state & DKSETUP) | |
177 | error = tsleep((caddr_t)tp, TTOPRI, ttopen, 0); | |
178 | while (error == 0 && tsp->dc_state & DKSETUP) | |
179 | error = tsleep((caddr_t)tsp, TTOPRI, ttopen, 0); | |
398dc04d | 180 | splx(s) ; |
e9036de0 MK |
181 | if (error) |
182 | return (error); | |
398dc04d MK |
183 | if ((dk_status(chan) & DK_RESET) || (dk_status(sp_chan) & DK_RESET)) |
184 | return EIO ; | |
185 | if (tp->d_error || tsp->d_error) | |
186 | return EIO ; | |
1059378b | 187 | error = copyout((caddr_t) tp->d_param, *(caddr_t *)data, |
398dc04d | 188 | 3*sizeof (short)); |
1059378b | 189 | if (error) return error; |
398dc04d MK |
190 | break ; |
191 | ||
192 | case DIOCSWAIT: | |
e9036de0 | 193 | error = dksplwait(chan); |
398dc04d MK |
194 | break ; |
195 | ||
196 | default: | |
197 | if ((cmd & DKIOCMASK) != DKIOCVAL) { | |
198 | return ENOTTY ; | |
199 | } | |
200 | if (cmd == DKIODIAL) { | |
1059378b | 201 | error = copyin(*(caddr_t *)data, (caddr_t) &dialreq, |
398dc04d | 202 | sizeof (struct diocdial)); |
1059378b KM |
203 | if (error) return error; |
204 | if (error = dkiodial(chan, dialreq.dialstring)) | |
205 | return error; | |
398dc04d MK |
206 | tp->dc_state |= DKSETUP ; |
207 | chanstat = dk_setup(minor(dev), (int) DKIOCREQ, 0, | |
208 | 0, 0, (int) u.u_uid, dkidone, (caddr_t)tp) ; | |
209 | } | |
210 | else { | |
1059378b | 211 | error = copyin(*(caddr_t *)data, (caddr_t) tp->d_param, |
398dc04d | 212 | 3*sizeof (short)); |
1059378b | 213 | if (error) return error; |
398dc04d | 214 | tp->dc_state |= DKSETUP ; |
586a8972 | 215 | chanstat = dk_setup(minor(dev), cmd, 0, 0, 0, |
398dc04d MK |
216 | (int) u.u_uid, dkidone, (caddr_t)tp) ; |
217 | } | |
218 | if (chanstat) { | |
219 | tp->dc_state &= ~DKSETUP ; | |
220 | return (chanstat < 0 ? ECONNREFUSED : chanstat); | |
221 | } | |
222 | s = spl5() ; | |
e9036de0 MK |
223 | error = 0; |
224 | while (error == 0 && tp->dc_state & DKSETUP) | |
225 | error = tsleep((caddr_t)(tp), TTOPRI, ttyout, 0) ; | |
398dc04d | 226 | splx(s) ; |
e9036de0 MK |
227 | if (error) |
228 | return error; | |
1059378b | 229 | error = copyout((caddr_t) tp->d_param, *(caddr_t *)data, |
398dc04d | 230 | 3*sizeof (short)); |
1059378b | 231 | if (error) return error; |
398dc04d MK |
232 | if (dk_status(minor(dev)) & DK_RESET) |
233 | return ENETRESET ; | |
234 | if (tp->d_error) | |
235 | return EIO ; | |
236 | break ; | |
237 | } | |
1059378b | 238 | return error; |
398dc04d MK |
239 | } |
240 | ||
241 | #define DS_SIZE 64 | |
242 | static | |
243 | dkiodial(chan, user_ds) | |
244 | register char *user_ds; | |
245 | { | |
246 | register caddr_t ds; | |
247 | register n; | |
248 | register struct mbuf *mb; | |
249 | int u_count; | |
250 | ||
251 | mb = m_get(M_WAIT, DKMT_DATA); | |
252 | if (mb == NULL) return ENOBUFS; | |
253 | ds = mtod(mb, caddr_t); | |
254 | for (u_count = 0; u_count < MLEN - 6; u_count++) { | |
255 | *ds = *user_ds; | |
256 | if (*ds == '\n' || *ds == '\0') break; | |
257 | ds++; | |
258 | user_ds++; | |
259 | } | |
260 | *ds = '\n'; | |
261 | u_count++; | |
262 | ||
263 | /* add uid in char decimal */ | |
264 | ||
265 | ds++; | |
266 | u_count++; | |
267 | for (n = u.u_uid; n /= 10; ds++) u_count++; | |
268 | for (n = u.u_uid;; ds--) { | |
269 | *ds = n % 10 + '0'; | |
270 | if ((n /= 10) == 0) break; | |
271 | } | |
272 | ||
273 | mb->m_len = u_count; | |
274 | if (dk_xmit(chan, mb, 1, 0, (int (*)()) 0, (caddr_t) 0) == 0) { | |
275 | return(EIO); | |
276 | } | |
277 | else return(0); | |
278 | } | |
279 | /* | |
280 | * End action for ioctl completion | |
281 | */ | |
282 | /*ARGSUSED*/ | |
283 | dkidone(tp, chan, err, p0, p1, p2) | |
284 | register struct dkdev *tp ; | |
285 | short chan, p0, p1, p2 ; | |
286 | { | |
287 | tp->d_error = err ; | |
288 | tp->d_param[0] = p0 ; | |
289 | tp->d_param[1] = p1 ; | |
290 | tp->d_param[2] = p2 ; | |
291 | tp->dc_state &= ~DKSETUP ; | |
292 | wakeup((caddr_t)tp) ; | |
293 | } | |
294 | ||
295 | ||
296 | ||
297 | ||
298 | /*ARGSUSED*/ | |
299 | dkopen(dev, flag) | |
300 | { | |
9342689a JH |
301 | USES_VOP_LOCK; |
302 | USES_VOP_UNLOCK; | |
398dc04d MK |
303 | register struct dkdev *tp; |
304 | register chan; | |
75a5515b KM |
305 | register struct nameidata *ndp = &u.u_nd; /* XXX */ |
306 | struct proc *p = u.u_procp; /* XXX */ | |
307 | struct vnode *vp; | |
398dc04d | 308 | struct file *fp; |
75a5515b | 309 | int m, error; |
398dc04d MK |
310 | |
311 | #ifdef lint | |
312 | (void) dk_xint(0, 0); | |
313 | #endif | |
314 | dev = minor(dev); | |
315 | if (dev == 1) { | |
316 | return 0; /* Maintenance channel */ | |
317 | } | |
318 | ||
319 | chan = dev; | |
320 | if (chan >= dk_nchan) { | |
321 | /* debug */ log(LOG_ERR, "dkopen bad: chan>=NDKCHANS : %d\n",chan); | |
322 | return ENXIO; | |
323 | } | |
324 | ||
325 | tp = &dkdev[chan]; | |
326 | if ((tp->d_state & DKOPEN) == 0) | |
327 | tp->dc_state = 0 ; | |
76d640d7 | 328 | if (tp->d_state&DKXCLUDE && u.u_procp->p_ruid!=0) { |
398dc04d MK |
329 | return EBUSY; |
330 | } | |
331 | ||
332 | if ((m = dk_open(chan, (int (*)()) NULL)) < 0) | |
333 | return -m; | |
334 | ||
335 | ||
336 | /* | |
337 | * Channel 0 is reserved for maintenance. | |
338 | * An open on channel 0 is interpreted as a request | |
339 | * for an unused channel. | |
340 | */ | |
341 | if (chan==0) { | |
342 | char dname[30]; | |
343 | ||
344 | chan = m ; | |
345 | tp = &dkdev[chan] ; | |
346 | tp->dc_state = 0 ; | |
347 | /* | |
1059378b | 348 | * throw away vnode for dk0. (/dev/dk/dial) |
398dc04d MK |
349 | * Build standard name of new one, and ask namei for it. |
350 | */ | |
75a5515b | 351 | fp = u.u_ofile[-1 - p->p_dupfd]; |
398dc04d MK |
352 | |
353 | dksnamer(dname, chan); | |
354 | /* log(LOG_ERR, "dname=%s chan=%d\n", dname, chan); */ | |
1059378b | 355 | ndp->ni_nameiop = FOLLOW | LOOKUP | LOCKLEAF; |
398dc04d MK |
356 | ndp->ni_segflg = UIO_SYSSPACE; |
357 | ndp->ni_dirp = dname; | |
1059378b | 358 | if (error = namei(ndp)) { |
398dc04d | 359 | (void) dk_close(chan) ; |
1059378b | 360 | return (error); |
398dc04d MK |
361 | } |
362 | ||
363 | /* Give back old one */ | |
1059378b KM |
364 | vp = (struct vnode *) fp->f_data; |
365 | VOP_LOCK(vp); | |
366 | vput(vp); | |
398dc04d | 367 | |
1059378b KM |
368 | vp = ndp->ni_vp; |
369 | fp->f_data = (caddr_t) vp; | |
370 | VOP_UNLOCK(vp); | |
398dc04d MK |
371 | } |
372 | if ((tp->d_state & DKOPEN) == 0) { | |
373 | tp->d_state |= DKOPEN ; | |
374 | tp->dc_state = 0; | |
375 | tp->d_rmode = 0 ; | |
376 | tp->d_xctl = 0 ; | |
377 | tp->d_pgrp = 0; | |
378 | } | |
379 | tp->d_prot |= DpURP; | |
380 | return 0; | |
381 | } | |
382 | ||
383 | /* Policy decision here -- standard name of dk file known to this routine */ | |
384 | dksnamer(s, n) register char *s; | |
385 | { | |
386 | register char *p = "/dev/dk/dk"; | |
387 | ||
388 | while (*s++ = *p++) | |
389 | ; | |
390 | s--; | |
391 | *s++ = '0' + (n/100); n %= 100; | |
392 | *s++ = '0' + (n/10); n %= 10; | |
393 | *s++ = '0' + n; | |
394 | *s = '\0'; | |
395 | } | |
396 | ||
397 | /* | |
398 | * Close a channel: | |
399 | */ | |
400 | ||
401 | /*ARGSUSED*/ | |
402 | dkclose(dev, flag) | |
403 | dev_t dev; | |
404 | int flag; | |
405 | { | |
406 | register struct dkdev *tp; | |
407 | extern wakeup() ; | |
408 | extern brelse() ; | |
409 | short s, chan ; | |
410 | int i, cl = 0; | |
411 | ||
412 | chan = minor(dev); | |
413 | tp = &dkdev[chan]; | |
414 | if (chan == 1) { | |
415 | return 0; /* Maintenance channel */ | |
416 | } | |
417 | s = spl5() ; | |
418 | if (u.u_signal[SIGKILL] != SIG_IGN) { /* detect close from exit() */ | |
419 | while (tp->d_bufct) { | |
420 | tp->d_state |= DKWAIT ; | |
e9036de0 MK |
421 | if (tsleep((caddr_t)(&tp->d_state), TTOPRI, ttyout, 0)) |
422 | break; | |
398dc04d MK |
423 | } |
424 | } | |
425 | else if (tp->d_bufct) | |
426 | /* Hmm -- buffers queued. Let's wait 15 seconds max */ | |
427 | for (i = 0; tp->d_bufct && i < 15; i++) { | |
428 | tp->d_state |= DKWAIT ; | |
e9036de0 MK |
429 | if (tsleep((caddr_t)(&tp->d_state), TTOPRI, ttyout, hz)) |
430 | break; | |
398dc04d MK |
431 | } |
432 | splx(s) ; | |
433 | tp->dc_state = 0; | |
434 | tp->d_rmode = 0; | |
435 | tp->d_prot &= ~DpURP; | |
436 | if(!tp->d_prot){ | |
437 | cl = dk_close(chan); | |
438 | (void) dk_takedown(chan); | |
439 | tp->d_state = 0; | |
440 | } | |
441 | return -cl; | |
442 | } | |
443 | ||
444 | dkread(dev, uio) | |
445 | dev_t dev ; | |
446 | struct uio *uio; | |
447 | { | |
448 | register struct dkdev *tp ; | |
449 | int err; | |
450 | ||
451 | M_ON(Mread) ; | |
452 | tp = &dkdev[minor(dev)] ; | |
453 | err = dkuread(minor(dev), uio) ; | |
454 | tp->d_rresid = uio->uio_resid ; | |
455 | M_OFF(Mread) ; | |
456 | return err; | |
457 | } | |
458 | ||
459 | ||
460 | dkwrite(dev, uio) | |
461 | struct uio *uio; | |
462 | dev_t dev ; | |
463 | { | |
464 | int err; | |
465 | ||
466 | M_ON(Mwrite) ; | |
467 | err = dkuwrite(minor(dev), uio) ; | |
468 | M_OFF(Mwrite) ; | |
469 | return err; | |
470 | } | |
471 | ||
472 | #endif |