[BACK]Return to uhid.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / dev / usb

Annotation of src/sys/dev/usb/uhid.c, Revision 1.10

1.10    ! augustss    1: /*     $NetBSD: uhid.c,v 1.9 1998/12/03 20:43:19 augustss Exp $        */
1.1       augustss    2:
                      3: /*
                      4:  * Copyright (c) 1998 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
                      6:  *
1.6       augustss    7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Lennart Augustsson (augustss@carlstedt.se) at
                      9:  * Carlstedt Research & Technology.
1.1       augustss   10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. All advertising materials mentioning features or use of this software
                     20:  *    must display the following acknowledgement:
                     21:  *        This product includes software developed by the NetBSD
                     22:  *        Foundation, Inc. and its contributors.
                     23:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     24:  *    contributors may be used to endorse or promote products derived
                     25:  *    from this software without specific prior written permission.
                     26:  *
                     27:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     28:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     29:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     30:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     31:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     32:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     33:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     34:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     35:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     36:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     37:  * POSSIBILITY OF SUCH DAMAGE.
                     38:  */
                     39:
                     40:
                     41: #include <sys/param.h>
                     42: #include <sys/systm.h>
                     43: #include <sys/kernel.h>
                     44: #include <sys/malloc.h>
                     45: #include <sys/device.h>
                     46: #include <sys/ioctl.h>
                     47: #include <sys/tty.h>
                     48: #include <sys/file.h>
                     49: #include <sys/select.h>
                     50: #include <sys/proc.h>
                     51: #include <sys/vnode.h>
                     52: #include <sys/device.h>
                     53: #include <sys/poll.h>
                     54:
                     55: #include <dev/usb/usb.h>
                     56: #include <dev/usb/usbhid.h>
                     57:
                     58: #include <dev/usb/usbdi.h>
                     59: #include <dev/usb/usbdi_util.h>
                     60: #include <dev/usb/hid.h>
                     61: #include <dev/usb/usb_quirks.h>
                     62:
                     63: #ifdef USB_DEBUG
                     64: #define DPRINTF(x)     if (uhiddebug) printf x
                     65: #define DPRINTFN(n,x)  if (uhiddebug>(n)) printf x
                     66: int    uhiddebug = 0;
                     67: #else
                     68: #define DPRINTF(x)
                     69: #define DPRINTFN(n,x)
                     70: #endif
                     71:
                     72: struct uhid_softc {
                     73:        struct device sc_dev;           /* base device */
                     74:        usbd_interface_handle sc_iface; /* interface */
                     75:        usbd_pipe_handle sc_intrpipe;   /* interrupt pipe */
                     76:        int sc_ep_addr;
                     77:
                     78:        int sc_isize;
                     79:        int sc_osize;
1.2       augustss   80:        int sc_fsize;
1.1       augustss   81:        u_int8_t sc_iid;
                     82:        u_int8_t sc_oid;
1.2       augustss   83:        u_int8_t sc_fid;
1.1       augustss   84:
                     85:        char *sc_ibuf;
                     86:        char *sc_obuf;
                     87:
                     88:        void *sc_repdesc;
                     89:        int sc_repdesc_size;
                     90:
                     91:        struct clist sc_q;
                     92:        struct selinfo sc_rsel;
                     93:        u_char sc_state;        /* driver state */
                     94: #define        UHID_OPEN       0x01    /* device is open */
1.5       augustss   95: #define        UHID_ASLP       0x02    /* waiting for device data */
1.1       augustss   96: #define UHID_NEEDCLEAR 0x04    /* needs clearing endpoint stall */
1.2       augustss   97: #define UHID_IMMED     0x08    /* return read data immediately */
1.1       augustss   98:        int sc_disconnected;    /* device is gone */
                     99: };
                    100:
                    101: #define        UHIDUNIT(dev)   (minor(dev))
                    102: #define        UHID_CHUNK      128     /* chunk size for read */
                    103: #define        UHID_BSIZE      1020    /* buffer size */
                    104:
                    105: int uhid_match __P((struct device *, struct cfdata *, void *));
                    106: void uhid_attach __P((struct device *, struct device *, void *));
                    107:
                    108: int uhidopen __P((dev_t, int, int, struct proc *));
                    109: int uhidclose __P((dev_t, int, int, struct proc *p));
                    110: int uhidread __P((dev_t, struct uio *uio, int));
                    111: int uhidwrite __P((dev_t, struct uio *uio, int));
                    112: int uhidioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
                    113: int uhidpoll __P((dev_t, int, struct proc *));
                    114: void uhid_intr __P((usbd_request_handle, usbd_private_handle, usbd_status));
                    115: void uhid_disco __P((void *));
                    116:
                    117: extern struct cfdriver uhid_cd;
                    118:
                    119: struct cfattach uhid_ca = {
                    120:        sizeof(struct uhid_softc), uhid_match, uhid_attach
                    121: };
                    122:
                    123: int
                    124: uhid_match(parent, match, aux)
                    125:        struct device *parent;
                    126:        struct cfdata *match;
                    127:        void *aux;
                    128: {
                    129:        struct usb_attach_arg *uaa = aux;
                    130:        usb_interface_descriptor_t *id;
                    131:
                    132:        if (!uaa->iface)
                    133:                return (UMATCH_NONE);
                    134:        id = usbd_get_interface_descriptor(uaa->iface);
                    135:        if (!id || id->bInterfaceClass != UCLASS_HID)
                    136:                return (UMATCH_NONE);
                    137:        return (UMATCH_IFACECLASS_GENERIC);
                    138: }
                    139:
                    140: void
                    141: uhid_attach(parent, self, aux)
                    142:        struct device *parent;
                    143:        struct device *self;
                    144:        void *aux;
                    145: {
                    146:        struct uhid_softc *sc = (struct uhid_softc *)self;
                    147:        struct usb_attach_arg *uaa = aux;
                    148:        usbd_interface_handle iface = uaa->iface;
                    149:        usb_interface_descriptor_t *id;
                    150:        usb_endpoint_descriptor_t *ed;
                    151:        int size;
                    152:        void *desc;
                    153:        usbd_status r;
                    154:        char devinfo[1024];
                    155:
                    156:        sc->sc_disconnected = 1;
                    157:        sc->sc_iface = iface;
                    158:        id = usbd_get_interface_descriptor(iface);
                    159:        usbd_devinfo(uaa->device, 0, devinfo);
1.8       augustss  160:        printf("\n%s: %s, iclass %d/%d\n", sc->sc_dev.dv_xname,
1.7       augustss  161:               devinfo, id->bInterfaceClass, id->bInterfaceSubClass);
1.1       augustss  162:        ed = usbd_interface2endpoint_descriptor(iface, 0);
                    163:        if (!ed) {
                    164:                printf("%s: could not read endpoint descriptor\n",
                    165:                       sc->sc_dev.dv_xname);
                    166:                return;
                    167:        }
                    168:
                    169:        DPRINTFN(10,("uhid_attach: \
                    170: bLength=%d bDescriptorType=%d bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d bInterval=%d\n",
                    171:               ed->bLength, ed->bDescriptorType, ed->bEndpointAddress & UE_ADDR,
                    172:               ed->bEndpointAddress & UE_IN ? "in" : "out",
                    173:               ed->bmAttributes & UE_XFERTYPE,
                    174:               UGETW(ed->wMaxPacketSize), ed->bInterval));
                    175:
                    176:        if ((ed->bEndpointAddress & UE_IN) != UE_IN ||
                    177:            (ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
                    178:                printf("%s: unexpected endpoint\n",
                    179:                       sc->sc_dev.dv_xname);
                    180:                return;
                    181:        }
                    182:
                    183:        sc->sc_ep_addr = ed->bEndpointAddress;
                    184:        sc->sc_disconnected = 0;
                    185:
                    186:        r = usbd_alloc_report_desc(uaa->iface, &desc, &size, M_USB);
                    187:        if (r != USBD_NORMAL_COMPLETION) {
                    188:                printf("%s: no report descriptor\n", sc->sc_dev.dv_xname);
                    189:                return;
                    190:        }
                    191:
                    192:        (void)usbd_set_idle(iface, 0, 0);
                    193:
1.2       augustss  194:        sc->sc_isize = hid_report_size(desc, size, hid_input,   &sc->sc_iid);
                    195:        sc->sc_osize = hid_report_size(desc, size, hid_output,  &sc->sc_oid);
                    196:        sc->sc_fsize = hid_report_size(desc, size, hid_feature, &sc->sc_fid);
1.1       augustss  197:
                    198:        sc->sc_repdesc = desc;
                    199:        sc->sc_repdesc_size = size;
                    200: }
                    201:
                    202: void
                    203: uhid_disco(p)
                    204:        void *p;
                    205: {
                    206:        struct uhid_softc *sc = p;
1.3       augustss  207:
                    208:        DPRINTF(("ums_hid: sc=%p\n", sc));
                    209:        usbd_abort_pipe(sc->sc_intrpipe);
1.1       augustss  210:        sc->sc_disconnected = 1;
                    211: }
                    212:
                    213: void
                    214: uhid_intr(reqh, addr, status)
                    215:        usbd_request_handle reqh;
                    216:        usbd_private_handle addr;
                    217:        usbd_status status;
                    218: {
                    219:        struct uhid_softc *sc = addr;
                    220:
                    221:        DPRINTFN(5, ("uhid_intr: status=%d\n", status));
                    222:        DPRINTFN(5, ("uhid_intr: data = %02x %02x %02x\n",
                    223:                     sc->sc_ibuf[0], sc->sc_ibuf[1], sc->sc_ibuf[2]));
                    224:
                    225:        if (status == USBD_CANCELLED)
                    226:                return;
                    227:
                    228:        if (status != USBD_NORMAL_COMPLETION) {
                    229:                DPRINTF(("uhid_intr: status=%d\n", status));
                    230:                sc->sc_state |= UHID_NEEDCLEAR;
                    231:                return;
                    232:        }
                    233:
                    234:        (void) b_to_q(sc->sc_ibuf, sc->sc_isize, &sc->sc_q);
                    235:
                    236:        if (sc->sc_state & UHID_ASLP) {
                    237:                sc->sc_state &= ~UHID_ASLP;
                    238:                DPRINTFN(5, ("uhid_intr: waking %p\n", sc));
                    239:                wakeup((caddr_t)sc);
                    240:        }
                    241:        selwakeup(&sc->sc_rsel);
                    242: }
                    243:
                    244: int
                    245: uhidopen(dev, flag, mode, p)
                    246:        dev_t dev;
                    247:        int flag;
                    248:        int mode;
                    249:        struct proc *p;
                    250: {
                    251:        int unit = UHIDUNIT(dev);
                    252:        struct uhid_softc *sc;
                    253:        usbd_status r;
                    254:
                    255:        if (unit >= uhid_cd.cd_ndevs)
                    256:                return ENXIO;
                    257:        sc = uhid_cd.cd_devs[unit];
                    258:        if (!sc)
                    259:                return ENXIO;
                    260:
                    261:        DPRINTF(("uhidopen: sc=%p, disco=%d\n", sc, sc->sc_disconnected));
                    262:
                    263:        if (sc->sc_disconnected)
                    264:                return (EIO);
                    265:
                    266:        if (sc->sc_state & UHID_OPEN)
                    267:                return EBUSY;
                    268:
                    269:        if (clalloc(&sc->sc_q, UHID_BSIZE, 0) == -1)
                    270:                return ENOMEM;
                    271:
                    272:        sc->sc_state |= UHID_OPEN;
1.2       augustss  273:        sc->sc_state &= ~UHID_IMMED;
1.1       augustss  274:
                    275:        sc->sc_ibuf = malloc(sc->sc_isize, M_USB, M_WAITOK);
                    276:        sc->sc_obuf = malloc(sc->sc_osize, M_USB, M_WAITOK);
                    277:
                    278:        /* Set up interrupt pipe. */
                    279:        r = usbd_open_pipe_intr(sc->sc_iface, sc->sc_ep_addr,
                    280:                                USBD_SHORT_XFER_OK,
                    281:                                &sc->sc_intrpipe, sc, sc->sc_ibuf,
                    282:                                sc->sc_isize, uhid_intr);
                    283:        if (r != USBD_NORMAL_COMPLETION) {
                    284:                DPRINTF(("uhidopen: usbd_open_pipe_intr failed, error=%d\n",r));
                    285:                sc->sc_state &= ~UHID_OPEN;
                    286:                return (EIO);
                    287:        }
                    288:        usbd_set_disco(sc->sc_intrpipe, uhid_disco, sc);
                    289:
                    290:        return 0;
                    291: }
                    292:
                    293: int
                    294: uhidclose(dev, flag, mode, p)
                    295:        dev_t dev;
                    296:        int flag;
                    297:        int mode;
                    298:        struct proc *p;
                    299: {
                    300:        struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
                    301:
                    302:        if (sc->sc_disconnected)
                    303:                return (EIO);
                    304:
                    305:        DPRINTF(("uhidclose: sc=%p\n", sc));
                    306:
                    307:        /* Disable interrupts. */
                    308:        usbd_abort_pipe(sc->sc_intrpipe);
                    309:        usbd_close_pipe(sc->sc_intrpipe);
                    310:
                    311:        sc->sc_state &= ~UHID_OPEN;
                    312:
                    313:        clfree(&sc->sc_q);
                    314:
                    315:        free(sc->sc_ibuf, M_USB);
                    316:        free(sc->sc_obuf, M_USB);
                    317:
                    318:        return 0;
                    319: }
                    320:
                    321: int
                    322: uhidread(dev, uio, flag)
                    323:        dev_t dev;
                    324:        struct uio *uio;
                    325:        int flag;
                    326: {
                    327:        struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
                    328:        int s;
                    329:        int error = 0;
                    330:        size_t length;
                    331:        u_char buffer[UHID_CHUNK];
1.2       augustss  332:        usbd_status r;
1.1       augustss  333:
                    334:        if (sc->sc_disconnected)
                    335:                return (EIO);
                    336:
                    337:        DPRINTFN(1, ("uhidread\n"));
1.2       augustss  338:        if (sc->sc_state & UHID_IMMED) {
                    339:                DPRINTFN(1, ("uhidread immed\n"));
                    340:
                    341:                r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
                    342:                                    sc->sc_iid, sc->sc_ibuf, sc->sc_isize);
                    343:                if (r != USBD_NORMAL_COMPLETION)
                    344:                        return (EIO);
                    345:                return (uiomove(buffer, sc->sc_isize, uio));
                    346:        }
                    347:
1.10    ! augustss  348:        s = splusb();
1.1       augustss  349:        while (sc->sc_q.c_cc == 0) {
                    350:                if (flag & IO_NDELAY) {
                    351:                        splx(s);
                    352:                        return EWOULDBLOCK;
                    353:                }
                    354:                sc->sc_state |= UHID_ASLP;
                    355:                DPRINTFN(5, ("uhidread: sleep on %p\n", sc));
                    356:                error = tsleep((caddr_t)sc, PZERO | PCATCH, "uhidrea", 0);
                    357:                DPRINTFN(5, ("uhidread: woke, error=%d\n", error));
                    358:                if (error) {
                    359:                        sc->sc_state &= ~UHID_ASLP;
                    360:                        splx(s);
                    361:                        return (error);
                    362:                }
                    363:                if (sc->sc_state & UHID_NEEDCLEAR) {
                    364:                        DPRINTFN(-1,("uhidread: clearing stall\n"));
                    365:                        sc->sc_state &= ~UHID_NEEDCLEAR;
                    366:                        usbd_clear_endpoint_stall(sc->sc_intrpipe);
                    367:                }
                    368:        }
                    369:        splx(s);
                    370:
                    371:        /* Transfer as many chunks as possible. */
                    372:        while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0) {
                    373:                length = min(sc->sc_q.c_cc, uio->uio_resid);
                    374:                if (length > sizeof(buffer))
                    375:                        length = sizeof(buffer);
                    376:
                    377:                /* Remove a small chunk from the input queue. */
                    378:                (void) q_to_b(&sc->sc_q, buffer, length);
                    379:                DPRINTFN(5, ("uhidread: got %d chars\n", length));
                    380:
                    381:                /* Copy the data to the user process. */
                    382:                if ((error = uiomove(buffer, length, uio)) != 0)
                    383:                        break;
                    384:        }
                    385:
                    386:        return (error);
                    387: }
                    388:
                    389: int
                    390: uhidwrite(dev, uio, flag)
                    391:        dev_t dev;
                    392:        struct uio *uio;
                    393:        int flag;
                    394: {
                    395:        struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
                    396:        int error;
                    397:        int size;
                    398:        usbd_status r;
                    399:
                    400:        if (sc->sc_disconnected)
                    401:                return (EIO);
                    402:
                    403:        DPRINTFN(1, ("uhidwrite\n"));
                    404:
                    405:        size = sc->sc_osize;
                    406:        error = 0;
                    407:        while (uio->uio_resid > 0) {
                    408:                if (uio->uio_resid != size)
                    409:                        return (0);
                    410:                if ((error = uiomove(sc->sc_obuf, size, uio)) != 0)
                    411:                        break;
                    412:                if (sc->sc_oid)
                    413:                        r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
                    414:                                            sc->sc_obuf[0],
                    415:                                            sc->sc_obuf+1, size-1);
                    416:                else
                    417:                        r = usbd_set_report(sc->sc_iface, UHID_OUTPUT_REPORT,
                    418:                                            0, sc->sc_obuf, size);
                    419:                if (r != USBD_NORMAL_COMPLETION) {
                    420:                        error = EIO;
                    421:                        break;
                    422:                }
                    423:        }
                    424:        return (error);
                    425: }
                    426:
                    427: int
                    428: uhidioctl(dev, cmd, addr, flag, p)
                    429:        dev_t dev;
                    430:        u_long cmd;
                    431:        caddr_t addr;
                    432:        int flag;
                    433:        struct proc *p;
                    434: {
                    435:        struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
                    436:        struct usb_ctl_report_desc *rd;
1.2       augustss  437:        struct usb_ctl_report *re;
                    438:        int size, id;
                    439:        usbd_status r;
1.1       augustss  440:
                    441:        if (sc->sc_disconnected)
                    442:                return (EIO);
                    443:
                    444:        DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd));
                    445:        switch (cmd) {
1.2       augustss  446:        case FIONBIO:
                    447:                /* All handled in the upper FS layer. */
                    448:                break;
                    449:
1.1       augustss  450:        case USB_GET_REPORT_DESC:
                    451:                rd = (struct usb_ctl_report_desc *)addr;
                    452:                size = min(sc->sc_repdesc_size, sizeof rd->data);
                    453:                rd->size = size;
                    454:                memcpy(rd->data, sc->sc_repdesc, size);
                    455:                break;
1.2       augustss  456:
                    457:        case USB_SET_IMMED:
1.9       augustss  458:                if (*(int *)addr) {
                    459:                        /* XXX should read into ibuf, but does it matter */
                    460:                        r = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT,
                    461:                                            sc->sc_iid, sc->sc_ibuf,
                    462:                                            sc->sc_isize);
                    463:                        if (r != USBD_NORMAL_COMPLETION)
                    464:                                return (EOPNOTSUPP);
1.2       augustss  465:                        sc->sc_state |=  UHID_IMMED;
1.9       augustss  466:                } else
1.2       augustss  467:                        sc->sc_state &= ~UHID_IMMED;
                    468:                break;
                    469:
                    470:        case USB_GET_REPORT:
                    471:                re = (struct usb_ctl_report *)addr;
                    472:                switch (re->report) {
                    473:                case UHID_INPUT_REPORT:
                    474:                        size = sc->sc_isize;
                    475:                        id = sc->sc_iid;
                    476:                        break;
                    477:                case UHID_OUTPUT_REPORT:
                    478:                        size = sc->sc_osize;
                    479:                        id = sc->sc_oid;
                    480:                        break;
                    481:                case UHID_FEATURE_REPORT:
                    482:                        size = sc->sc_fsize;
                    483:                        id = sc->sc_fid;
                    484:                        break;
                    485:                default:
                    486:                        return (EINVAL);
                    487:                }
                    488:                r = usbd_get_report(sc->sc_iface, re->report, id,
                    489:                                    re->data, size);
                    490:                if (r != USBD_NORMAL_COMPLETION)
                    491:                        return (EIO);
                    492:                break;
                    493:
1.1       augustss  494:        default:
                    495:                return (EINVAL);
                    496:        }
                    497:        return (0);
                    498: }
                    499:
                    500: int
                    501: uhidpoll(dev, events, p)
                    502:        dev_t dev;
                    503:        int events;
                    504:        struct proc *p;
                    505: {
                    506:        struct uhid_softc *sc = uhid_cd.cd_devs[UHIDUNIT(dev)];
                    507:        int revents = 0;
                    508:        int s;
                    509:
                    510:        if (sc->sc_disconnected)
                    511:                return (EIO);
                    512:
1.10    ! augustss  513:        s = splusb();
1.1       augustss  514:        if (events & (POLLOUT | POLLWRNORM))
                    515:                revents |= events & (POLLOUT | POLLWRNORM);
1.4       veego     516:        if (events & (POLLIN | POLLRDNORM)) {
1.1       augustss  517:                if (sc->sc_q.c_cc > 0)
                    518:                        revents |= events & (POLLIN | POLLRDNORM);
                    519:                else
                    520:                        selrecord(p, &sc->sc_rsel);
1.4       veego     521:        }
1.1       augustss  522:
                    523:        splx(s);
                    524:        return (revents);
                    525: }

CVSweb <webmaster@jp.NetBSD.org>