/* * dvb-dibusb-usb.c is part of the driver for mobile USB Budget DVB-T devices * based on reference design made by DiBcom (http://www.dibcom.fr/) * * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de) * * see dvb-dibusb-core.c for more copyright details. * * This file contains functions for initializing and handling the * usb specific stuff. */ #include "dvb-dibusb.h" #include #include int dibusb_readwrite_usb(struct usb_dibusb *dib, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) { int actlen,ret = -ENOMEM; if (wbuf == NULL || wlen == 0) return -EINVAL; if ((ret = down_interruptible(&dib->usb_sem))) return ret; debug_dump(wbuf,wlen); ret = usb_bulk_msg(dib->udev,usb_sndbulkpipe(dib->udev, dib->dibdev->dev_cl->pipe_cmd), wbuf,wlen,&actlen, DIBUSB_I2C_TIMEOUT); if (ret) err("bulk message failed: %d (%d/%d)",ret,wlen,actlen); else ret = actlen != wlen ? -1 : 0; /* an answer is expected, and no error before */ if (!ret && rbuf && rlen) { ret = usb_bulk_msg(dib->udev,usb_rcvbulkpipe(dib->udev, dib->dibdev->dev_cl->pipe_cmd),rbuf,rlen,&actlen, DIBUSB_I2C_TIMEOUT); if (ret) err("recv bulk message failed: %d",ret); else { deb_alot("rlen: %d\n",rlen); debug_dump(rbuf,actlen); } } up(&dib->usb_sem); return ret; } /* * Cypress controls */ int dibusb_write_usb(struct usb_dibusb *dib, u8 *buf, u16 len) { return dibusb_readwrite_usb(dib,buf,len,NULL,0); } #if 0 /* * #if 0'ing the following functions as they are not in use _now_, * but probably will be sometime. */ /* * do not use this, just a workaround for a bug, * which will hopefully never occur :). */ int dibusb_interrupt_read_loop(struct usb_dibusb *dib) { u8 b[1] = { DIBUSB_REQ_INTR_READ }; return dibusb_write_usb(dib,b,1); } #endif /* * ioctl for the firmware */ static int dibusb_ioctl_cmd(struct usb_dibusb *dib, u8 cmd, u8 *param, int plen) { u8 b[34]; int size = plen > 32 ? 32 : plen; memset(b,0,34); b[0] = DIBUSB_REQ_SET_IOCTL; b[1] = cmd; if (size > 0) memcpy(&b[2],param,size); return dibusb_write_usb(dib,b,34); //2+size); } /* * ioctl for power control */ int dibusb_hw_wakeup(struct dvb_frontend *fe) { struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; u8 b[1] = { DIBUSB_IOCTL_POWER_WAKEUP }; deb_info("dibusb-device is getting up.\n"); switch (dib->dibdev->dev_cl->id) { case DTT200U: break; default: dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); break; } if (dib->fe_init) return dib->fe_init(fe); return 0; } int dibusb_hw_sleep(struct dvb_frontend *fe) { struct usb_dibusb *dib = (struct usb_dibusb *) fe->dvb->priv; u8 b[1] = { DIBUSB_IOCTL_POWER_SLEEP }; deb_info("dibusb-device is going to bed.\n"); /* workaround, something is wrong, when dibusb 1.1 device are going to bed too late */ switch (dib->dibdev->dev_cl->id) { case DIBUSB1_1: case NOVAT_USB2: case DTT200U: break; default: dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_POWER_MODE, b,1); break; } if (dib->fe_sleep) return dib->fe_sleep(fe); return 0; } int dibusb_set_streaming_mode(struct usb_dibusb *dib,u8 mode) { u8 b[2] = { DIBUSB_REQ_SET_STREAMING_MODE, mode }; return dibusb_readwrite_usb(dib,b,2,NULL,0); } static int dibusb_urb_kill(struct usb_dibusb *dib) { int i; deb_info("trying to kill urbs\n"); if (dib->init_state & DIBUSB_STATE_URB_SUBMIT) { for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { deb_info("killing URB no. %d.\n",i); /* stop the URB */ usb_kill_urb(dib->urb_list[i]); } } else deb_info(" URBs not killed.\n"); dib->init_state &= ~DIBUSB_STATE_URB_SUBMIT; return 0; } static int dibusb_urb_submit(struct usb_dibusb *dib) { int i,ret; if (dib->init_state & DIBUSB_STATE_URB_INIT) { for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { deb_info("submitting URB no. %d\n",i); if ((ret = usb_submit_urb(dib->urb_list[i],GFP_ATOMIC))) { err("could not submit buffer urb no. %d - get them all back\n",i); dibusb_urb_kill(dib); return ret; } dib->init_state |= DIBUSB_STATE_URB_SUBMIT; } } return 0; } int dibusb_streaming(struct usb_dibusb *dib,int onoff) { if (onoff) dibusb_urb_submit(dib); else dibusb_urb_kill(dib); switch (dib->dibdev->dev_cl->id) { case DIBUSB2_0: case DIBUSB2_0B: case NOVAT_USB2: case UMT2_0: if (onoff) return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_ENABLE_STREAM,NULL,0); else return dibusb_ioctl_cmd(dib,DIBUSB_IOCTL_CMD_DISABLE_STREAM,NULL,0); break; default: break; } return 0; } int dibusb_urb_init(struct usb_dibusb *dib) { int i,bufsize,def_pid_parse = 1; /* * when reloading the driver w/o replugging the device * a timeout occures, this helps */ usb_clear_halt(dib->udev,usb_sndbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_cmd)); usb_clear_halt(dib->udev,usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data)); /* allocate the array for the data transfer URBs */ dib->urb_list = kmalloc(dib->dibdev->dev_cl->urb_count*sizeof(struct urb *),GFP_KERNEL); if (dib->urb_list == NULL) return -ENOMEM; memset(dib->urb_list,0,dib->dibdev->dev_cl->urb_count*sizeof(struct urb *)); dib->init_state |= DIBUSB_STATE_URB_LIST; bufsize = dib->dibdev->dev_cl->urb_count*dib->dibdev->dev_cl->urb_buffer_size; deb_info("allocate %d bytes as buffersize for all URBs\n",bufsize); /* allocate the actual buffer for the URBs */ if ((dib->buffer = pci_alloc_consistent(NULL,bufsize,&dib->dma_handle)) == NULL) { deb_info("not enough memory.\n"); return -ENOMEM; } deb_info("allocation complete\n"); memset(dib->buffer,0,bufsize); dib->init_state |= DIBUSB_STATE_URB_BUF; /* allocate and submit the URBs */ for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { if (!(dib->urb_list[i] = usb_alloc_urb(0,GFP_ATOMIC))) { return -ENOMEM; } usb_fill_bulk_urb( dib->urb_list[i], dib->udev, usb_rcvbulkpipe(dib->udev,dib->dibdev->dev_cl->pipe_data), &dib->buffer[i*dib->dibdev->dev_cl->urb_buffer_size], dib->dibdev->dev_cl->urb_buffer_size, dibusb_urb_complete, dib); dib->urb_list[i]->transfer_flags = 0; dib->init_state |= DIBUSB_STATE_URB_INIT; } /* dib->pid_parse here contains the value of the module parameter */ /* decide if pid parsing can be deactivated: * is possible (by device type) and wanted (by user) */ switch (dib->dibdev->dev_cl->id) { case DIBUSB2_0: case DIBUSB2_0B: if (dib->udev->speed == USB_SPEED_HIGH && !dib->pid_parse) { def_pid_parse = 0; info("running at HIGH speed, will deliver the complete TS."); } else info("will use pid_parsing."); break; default: break; } /* from here on it contains the device and user decision */ dib->pid_parse = def_pid_parse; return 0; } int dibusb_urb_exit(struct usb_dibusb *dib) { int i; dibusb_urb_kill(dib); if (dib->init_state & DIBUSB_STATE_URB_LIST) { for (i = 0; i < dib->dibdev->dev_cl->urb_count; i++) { if (dib->urb_list[i] != NULL) { deb_info("freeing URB no. %d.\n",i); /* free the URBs */ usb_free_urb(dib->urb_list[i]); } } /* free the urb array */ kfree(dib->urb_list); dib->init_state &= ~DIBUSB_STATE_URB_LIST; } if (dib->init_state & DIBUSB_STATE_URB_BUF) pci_free_consistent(NULL, dib->dibdev->dev_cl->urb_buffer_size*dib->dibdev->dev_cl->urb_count, dib->buffer,dib->dma_handle); dib->init_state &= ~DIBUSB_STATE_URB_BUF; dib->init_state &= ~DIBUSB_STATE_URB_INIT; return 0; }