* better
[mascara-docs.git] / i386 / linux-2.3.21 / drivers / isdn / isdn_v110.c
blobae62378b875751b4b742a031730562db27ed0723
1 /* $Id: isdn_v110.c,v 1.2 1998/02/22 19:44:25 fritz Exp $
3 * Linux ISDN subsystem, V.110 related functions (linklevel).
5 * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * $Log: isdn_v110.c,v $
22 * Revision 1.2 1998/02/22 19:44:25 fritz
23 * Bugfixes and improvements regarding V.110, V.110 now running.
25 * Revision 1.1 1998/02/20 17:32:09 fritz
26 * First checkin (not yet completely functionable).
29 #include <linux/string.h>
30 #include <linux/kernel.h>
31 #include <linux/malloc.h>
32 #include <linux/mm.h>
34 #include <linux/isdn.h>
35 #include "isdn_v110.h"
37 #undef ISDN_V110_DEBUG
39 char *isdn_v110_revision = "$Revision: 1.2 $";
41 #define V110_38400 255
42 #define V110_19200 15
43 #define V110_9600 3
45 /* Die folgenden Daten sind fertig kodierte Matrizen, jeweils
46 als online und offline matrix für 9600, 19200 und 38400
48 static unsigned char V110_OnMatrix_9600[] =
49 {0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
50 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
51 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
52 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
54 static unsigned char V110_OffMatrix_9600[] =
55 {0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
56 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
60 static unsigned char V110_OnMatrix_19200[] =
61 {0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
62 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
64 static unsigned char V110_OffMatrix_19200[] =
65 {0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
66 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
68 static unsigned char V110_OnMatrix_38400[] =
69 {0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
71 static unsigned char V110_OffMatrix_38400[] =
72 {0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
75 /* FlipBits dreht die Reihenfolge von jeweils keylen bits in einem byte um.
76 Aus der Bitreihenfolge 76543210 werden bei keylen=4 die bits 45670123,
77 bei keylen=2 die bits 67452301. Dies ist notwendig, weil die reihenfolge
78 auf der isdn-leitung falsch herum ist.
81 static __inline unsigned char
82 FlipBits(unsigned char c, int keylen)
84 unsigned char b = c;
85 unsigned char bit = 128;
86 int i;
87 int j;
88 int hunks = (8 / keylen);
90 c = 0;
91 for (i = 0; i < hunks; i++) {
92 for (j = 0; j < keylen; j++) {
93 if (b & (bit >> j))
94 c |= bit >> (keylen - j - 1);
96 bit >>= keylen;
98 return c;
102 /* isdn_v110_open allocates and initializes private V.110 data
103 * structures and returns a pointer to these.
105 static isdn_v110_stream *
106 isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
108 int i;
109 isdn_v110_stream *v;
111 if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_KERNEL)) == NULL)
112 return NULL;
113 memset(v, 0, sizeof(isdn_v110_stream));
114 v->key = key;
115 v->nbits = 0;
116 for (i = 0; key & (1 << i); i++)
117 v->nbits++;
119 v->nbytes = 8 / v->nbits;
120 v->decodelen = 0;
122 switch (key) {
123 case V110_38400:
124 v->OnlineFrame = V110_OnMatrix_38400;
125 v->OfflineFrame = V110_OffMatrix_38400;
126 break;
127 case V110_19200:
128 v->OnlineFrame = V110_OnMatrix_19200;
129 v->OfflineFrame = V110_OffMatrix_19200;
130 break;
131 default:
132 v->OnlineFrame = V110_OnMatrix_9600;
133 v->OfflineFrame = V110_OffMatrix_9600;
134 break;
136 v->framelen = v->nbytes * 10;
137 v->SyncInit = 5;
138 v->introducer = 0;
139 v->dbit = 1;
140 v->b = 0;
141 v->skbres = hdrlen;
142 v->maxsize = maxsize - hdrlen;
143 if ((v->encodebuf = kmalloc(maxsize, GFP_KERNEL)) == NULL) {
144 kfree(v);
145 return NULL;
147 return v;
150 /* isdn_v110_close frees private V.110 data structures */
151 static void
152 isdn_v110_close(isdn_v110_stream * v)
154 if (v == NULL)
155 return;
156 #ifdef ISDN_V110_DEBUG
157 printk(KERN_DEBUG "v110 close\n");
158 #if 0
159 printk(KERN_DEBUG "isdn_v110_close: nbytes=%d\n", v->nbytes);
160 printk(KERN_DEBUG "isdn_v110_close: nbits=%d\n", v->nbits);
161 printk(KERN_DEBUG "isdn_v110_close: key=%d\n", v->key);
162 printk(KERN_DEBUG "isdn_v110_close: SyncInit=%d\n", v->SyncInit);
163 printk(KERN_DEBUG "isdn_v110:close: decodelen=%d\n", v->decodelen);
164 printk(KERN_DEBUG "isdn_v110_close: framelen=%d\n", v->framelen);
165 #endif
166 #endif
167 kfree(v->encodebuf);
168 kfree(v);
172 /* ValidHeaderBytes prüft, wieviele bytes in v->decodebuf gültig sind */
174 static int
175 ValidHeaderBytes(isdn_v110_stream * v)
177 int i;
178 for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
179 if ((v->decodebuf[i] & v->key) != 0)
180 break;
181 return i;
184 /* SyncHeader schiebt den decodebuf pointer auf den nächsten gültigen header */
186 static void
187 SyncHeader(isdn_v110_stream * v)
189 unsigned char *rbuf = v->decodebuf;
190 int len = v->decodelen;
192 if (len == 0)
193 return;
194 for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */
195 if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */
196 break; /* jupp! */
197 if (len)
198 memcpy(v->decodebuf, rbuf, len);
200 v->decodelen = len;
201 #ifdef ISDN_V110_DEBUG
202 printk(KERN_DEBUG "isdn_v110: Header resync\n");
203 #endif
206 /* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
207 len is the number of matrix-lines. len must be a multiple of 10, i.e.
208 only complete matices must be given.
209 From these, netto data is extracted and returned in buf. The return-value
210 is the bytecount of the decoded data.
212 static int
213 DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
215 int line = 0;
216 int buflen = 0;
217 int mbit = 64;
218 int introducer = v->introducer;
219 int dbit = v->dbit;
220 unsigned char b = v->b;
222 while (line < len) { /* sind schon alle matrizenzeilen abgearbeitet? */
223 if ((line % 10) == 0) { /* die 0. zeile der matrix ist immer null ! */
224 if (m[line] != 0x00) { /* nicht 0 ? dann fehler! */
225 #ifdef ISDN_V110_DEBUG
226 printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
227 #endif
230 dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
231 v->introducer = 0; v->dbit = 1; v->b = 0;
232 return buflen; anzahl schon erzeugter daten zurückgeben!
235 line++; /* sonst die nächste matrixzeile nehmen */
236 continue;
237 } else if ((line % 10) == 5) { /* in zeile 5 stehen nur e-bits ! */
238 if ((m[line] & 0x70) != 0x30) { /* 011 muß am anfang stehen! */
239 #ifdef ISDN_V110_DEBUG
240 printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
241 #endif
242 /* dann einen return zu machen, ist auch irgendwie nicht das richtige! :-(
243 v->introducer = 0; v->dbit = 1; v->b = 0;
244 return buflen;
247 line++; /* alles klar, nächste zeile */
248 continue;
249 } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */
250 introducer = (m[line] & mbit) ? 0 : 1; /* aktuelles bit der matrix */
251 next_byte:
252 if (mbit > 2) { /* war es das letzte bit dieser matrixzeile ? */
253 mbit >>= 1; /* nein, nimm das nächste in dieser zeile */
254 continue;
255 } /* sonst links in der nächsten zeile anfangen */
256 mbit = 64;
257 line++;
258 continue;
259 } else { /* sonst müssen wir ein datenbit setzen */
260 if (m[line] & mbit) /* war das bit in der matrix gesetzt ? */
261 b |= dbit; /* ja, dann setz es auch im datenbyte */
262 else
263 b &= dbit - 1; /* nein, lösch bit im datenbyte */
264 if (dbit < 128) /* haben wir schon ein ganzes byte voll ? */
265 dbit <<= 1; /* nein, auf zum nächsten datenbit */
266 else { /* ein ganzes datenbyte ist voll */
267 buf[buflen++] = b; /* byte in den output buffer kopieren */
268 introducer = b = 0; /* Init der Introsequenz und des datenbytes */
269 dbit = 1; /* als nächstes suchen wir das nullte bit */
271 goto next_byte; /* suche das nächste bit in der matrix */
274 v->introducer = introducer;
275 v->dbit = dbit;
276 v->b = b;
277 return buflen; /* return anzahl der bytes im output buffer */
280 /* DecodeStream erhält vom input stream V110 kodierte Daten, die zu den
281 V110 frames zusammengepackt werden müssen. Die Daten können an diese
282 Schnittstelle so übergeben werden, wie sie von der Leitung kommen, ohne
283 darauf achten zu müssen, das frames usw. eingehalten werden.
285 struct sk_buff *
286 isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
288 int i;
289 int j;
290 int len;
291 unsigned char *v110_buf;
292 unsigned char *rbuf;
294 if (!skb) {
295 printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
296 return NULL;
298 rbuf = skb->data;
299 len = skb->len;
300 if (v == NULL) {
301 /* invalid handle, no chance to proceed */
302 printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
303 dev_kfree_skb(skb);
304 return NULL;
306 if (v->decodelen == 0) /* cache empty? */
307 for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */
308 if ((*rbuf & v->key) == 0)
309 break; /* found first byte */
310 if (len == 0) {
311 dev_kfree_skb(skb);
312 return NULL;
314 /* copy new data to decode-buffer */
315 memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
316 v->decodelen += len;
317 ReSync:
318 if (v->decodelen < v->nbytes) { /* got a new header ? */
319 dev_kfree_skb(skb);
320 return NULL; /* no, try later */
322 if (ValidHeaderBytes(v) != v->nbytes) { /* ist es ein ungültiger header ? */
323 SyncHeader(v); /* nein, such einen header */
324 goto ReSync;
326 len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
327 if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
328 printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
329 dev_kfree_skb(skb);
330 return NULL;
332 for (i = 0; i < len; i++) {
333 v110_buf[i] = 0;
334 for (j = 0; j < v->nbytes; j++)
335 v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
336 v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
338 v->decodelen = (v->decodelen % (10 * v->nbytes));
339 memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
341 skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
342 kfree(v110_buf);
343 if (skb->len)
344 return skb;
345 else {
346 kfree_skb(skb);
347 return NULL;
351 /* EncodeMatrix takes input data in buf, len is the bytecount.
352 Data is encoded into v110 frames in m. Return value is the number of
353 matrix-lines generated.
355 static int
356 EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
358 int line = 0;
359 int i = 0;
360 int mbit = 128;
361 int dbit = 1;
362 int introducer = 3;
363 int ibit[] = {0, 1, 1};
365 while ((i < len) && (line < mlen)) { /* solange noch input da ist */
366 switch (line % 10) { /* in welcher matrixzeile sind wir ? */
367 case 0:
368 m[line++] = 0x00; /* zeile 0 ist immer 0 */
369 mbit = 128; /* und es geht mit dem 7. bit weiter */
370 break;
371 case 5:
372 m[line++] = 0xbf; /* zeile 5 ist immer 10111111 */
373 mbit = 128; /* und es geht mit dem 7. bit weiter */
374 break;
376 if (line >= mlen) {
377 printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
378 return line;
380 next_bit:
381 switch (mbit) { /* ganz linkes oder rechtes bit ? */
382 case 1:
383 line++; /* ganz rechts ! dann in die nächste */
384 if (line >= mlen) {
385 printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
386 return line;
388 case 128:
389 m[line] = 128; /* ganz links byte auf 1000000 setzen */
390 mbit = 64; /* aktuelles bit in der matrixzeile */
391 continue;
393 if (introducer) { /* 110 sequenz setzen ? */
394 introducer--; /* ein digit weniger setzen */
395 m[line] |= ibit[introducer] ? mbit : 0; /* entsprechendes bit setzen */
396 mbit >>= 1; /* bit der matrixzeile >> 1 */
397 goto next_bit; /* und dort weiter machen */
398 } /* else datenbits in die matrix packen! */
399 m[line] |= (buf[i] & dbit) ? mbit : 0; /* datenbit in matrix setzen */
400 if (dbit == 128) { /* war es das letzte datenbit ? */
401 dbit = 1; /* dann mach beim nächsten weiter */
402 i++; /* nächste datenbyte des input buffers */
403 if (i < len) /* war es schon das letzte ? */
404 introducer = 3; /* nein, schreib den introducer 110 */
405 else { /* war das letzte datenbyte ! */
406 m[line] |= (mbit - 1) & 0xfe; /* setz restliche bits der zeile auf 1 */
407 break;
409 } else /* nicht das letzte datenbit */
410 dbit <<= 1; /* dann gehe zum nächsten datenbit */
411 mbit >>= 1; /* und setz bit der matrix weiter */
412 goto next_bit;
415 /* evtl. noch restliche zeilen in der matrix generieren... */
416 if ((line) && ((line + 10) < mlen))
417 switch (++line % 10) {
418 case 1:
419 m[line++] = 0xfe;
420 case 2:
421 m[line++] = 0xfe;
422 case 3:
423 m[line++] = 0xfe;
424 case 4:
425 m[line++] = 0xfe;
426 case 5:
427 m[line++] = 0xbf;
428 case 6:
429 m[line++] = 0xfe;
430 case 7:
431 m[line++] = 0xfe;
432 case 8:
433 m[line++] = 0xfe;
434 case 9:
435 m[line++] = 0xfe;
437 return line; /* soviele matrixzeilen sind es */
441 * Build a sync frame.
443 static struct sk_buff *
444 isdn_v110_sync(isdn_v110_stream *v)
446 struct sk_buff *skb;
448 if (v == NULL) {
449 /* invalid handle, no chance to proceed */
450 printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
451 return NULL;
453 if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
454 skb_reserve(skb, v->skbres);
455 memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
457 return skb;
461 * Build an idle frame.
463 static struct sk_buff *
464 isdn_v110_idle(isdn_v110_stream *v)
466 struct sk_buff *skb;
468 if (v == NULL) {
469 /* invalid handle, no chance to proceed */
470 printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
471 return NULL;
473 if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
474 skb_reserve(skb, v->skbres);
475 memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
477 return skb;
480 struct sk_buff *
481 isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
483 int i;
484 int j;
485 int rlen;
486 int mlen;
487 int olen;
488 int size;
489 int sval1;
490 int sval2;
491 int nframes;
492 unsigned char *v110buf;
493 unsigned char *rbuf;
494 struct sk_buff *nskb;
496 if (v == NULL) {
497 /* invalid handle, no chance to proceed */
498 printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
499 return NULL;
501 if (!skb) {
502 /* invalid skb, no chance to proceed */
503 printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
504 return NULL;
506 rlen = skb->len;
507 nframes = (rlen + 3) / 4;
508 v110buf = v->encodebuf;
509 if ((nframes * 40) > v->maxsize) {
510 size = v->maxsize;
511 rlen = v->maxsize / 40;
512 } else
513 size = nframes * 40;
514 if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
515 printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
516 return NULL;
518 skb_reserve(nskb, v->skbres + sizeof(int));
519 if (skb->len == 0) {
520 memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
521 *((int *)skb_push(nskb, sizeof(int))) = 0;
522 return nskb;
524 mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
525 /* jetzt noch jeweils 2 oder 4 bits auf den output stream verteilen! */
526 rbuf = skb_put(nskb, size);
527 olen = 0;
528 sval1 = 8 - v->nbits;
529 sval2 = v->key << sval1;
530 for (i = 0; i < mlen; i++) {
531 v110buf[i] = FlipBits(v110buf[i], v->nbits);
532 for (j = 0; j < v->nbytes; j++) {
533 if (size--)
534 *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
535 else {
536 printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
537 goto buffer_full;
539 olen++;
542 buffer_full:
543 skb_trim(nskb, olen);
544 *((int *)skb_push(nskb, sizeof(int))) = rlen;
545 return nskb;
549 isdn_v110_stat_callback(int idx, isdn_ctrl * c)
551 isdn_v110_stream *v = NULL;
552 int i;
553 int ret;
555 if (idx < 0)
556 return 0;
557 switch (c->command) {
558 case ISDN_STAT_BSENT:
559 /* Keep the send-queue of the driver filled
560 * with frames:
561 * If number of outstanding frames < 3,
562 * send down an Idle-Frame (or an Sync-Frame, if
563 * v->SyncInit != 0).
565 if (!(v = dev->v110[idx]))
566 return 0;
567 atomic_inc(&dev->v110use[idx]);
568 if (v->skbidle > 0) {
569 v->skbidle--;
570 ret = 1;
571 } else {
572 if (v->skbuser > 0)
573 v->skbuser--;
574 ret = 0;
576 for (i = v->skbuser + v->skbidle; i < 2; i++) {
577 struct sk_buff *skb;
578 if (v->SyncInit > 0)
579 skb = isdn_v110_sync(v);
580 else
581 skb = isdn_v110_idle(v);
582 if (skb) {
583 if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
584 dev_kfree_skb(skb);
585 break;
586 } else {
587 if (v->SyncInit)
588 v->SyncInit--;
589 v->skbidle++;
591 } else
592 break;
594 atomic_dec(&dev->v110use[idx]);
595 return ret;
596 case ISDN_STAT_DHUP:
597 case ISDN_STAT_BHUP:
598 while (1) {
599 atomic_inc(&dev->v110use[idx]);
600 if (atomic_dec_and_test(&dev->v110use[idx])) {
601 isdn_v110_close(dev->v110[idx]);
602 dev->v110[idx] = NULL;
603 break;
605 sti();
607 break;
608 case ISDN_STAT_BCONN:
609 if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
610 int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
611 int maxsize = dev->drv[c->driver]->interface->maxbufsize;
612 atomic_inc(&dev->v110use[idx]);
613 switch (dev->v110emu[idx]) {
614 case ISDN_PROTO_L2_V11096:
615 dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
616 break;
617 case ISDN_PROTO_L2_V11019:
618 dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
619 break;
620 case ISDN_PROTO_L2_V11038:
621 dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
622 break;
623 default:
625 if ((v = dev->v110[idx])) {
626 while (v->SyncInit) {
627 struct sk_buff *skb = isdn_v110_sync(v);
628 if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
629 dev_kfree_skb(skb);
630 /* Unable to send, try later */
631 break;
633 v->SyncInit--;
634 v->skbidle++;
636 } else
637 printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
638 atomic_dec(&dev->v110use[idx]);
640 break;
641 default:
642 return 0;
644 return 0;