diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-06-15 01:55:58 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-06-15 01:55:58 +0000 |
commit | 53b3988d474435254a3b053a68bb24ce9e439295 (patch) | |
tree | f8da8e40f01f4ad02bbd76b8c9920749b118235f /drivers/usb | |
parent | b0cb48abe83d1a4389ea938bf624f8baa82c5047 (diff) |
Merge with 2.3.99-pre9.
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/Config.in | 2 | ||||
-rw-r--r-- | drivers/usb/audio.c | 129 | ||||
-rw-r--r-- | drivers/usb/hub.c | 4 | ||||
-rw-r--r-- | drivers/usb/input.c | 8 | ||||
-rw-r--r-- | drivers/usb/ov511.c | 743 | ||||
-rw-r--r-- | drivers/usb/ov511.h | 139 | ||||
-rw-r--r-- | drivers/usb/pegasus.c | 50 | ||||
-rw-r--r-- | drivers/usb/printer.c | 45 | ||||
-rw-r--r-- | drivers/usb/serial/Makefile | 22 | ||||
-rw-r--r-- | drivers/usb/serial/digi_acceleport.c | 1164 | ||||
-rw-r--r-- | drivers/usb/serial/ftdi_sio.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/keyspan_pda.c | 4 | ||||
-rw-r--r-- | drivers/usb/serial/omninet.c | 6 | ||||
-rw-r--r-- | drivers/usb/serial/usbserial.c | 4 | ||||
-rw-r--r-- | drivers/usb/serial/visor.c | 7 | ||||
-rw-r--r-- | drivers/usb/serial/whiteheat.c | 6 | ||||
-rw-r--r-- | drivers/usb/uhci.c | 31 | ||||
-rw-r--r-- | drivers/usb/usb-ohci.c | 160 | ||||
-rw-r--r-- | drivers/usb/usb-ohci.h | 2 | ||||
-rw-r--r-- | drivers/usb/usb-storage.c | 356 | ||||
-rw-r--r-- | drivers/usb/usb-storage.h | 14 | ||||
-rw-r--r-- | drivers/usb/usb-uhci.c | 87 | ||||
-rw-r--r-- | drivers/usb/usb-uhci.h | 4 | ||||
-rw-r--r-- | drivers/usb/wacom.c | 2 |
24 files changed, 1778 insertions, 1217 deletions
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 3b376c365..74253ed99 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -43,7 +43,7 @@ comment 'USB Devices' dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB - dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB + dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI if [ "$CONFIG_USB_STORAGE" != "n" ]; then bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG fi diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 354c1d6cf..40903a9c2 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -3,7 +3,7 @@ /* * audio.c -- USB Audio Class driver * - * Copyright (C) 1999 + * Copyright (C) 1999, 2000 * Alan Cox (alan@lxorguk.ukuu.org.uk) * Thomas Sailer (sailer@ife.ee.ethz.ch) * @@ -59,6 +59,21 @@ * 1999-12-20: Fix bad bug in conversion to per interface probing. * disconnect was called multiple times for the audio device, * leading to a premature freeing of the audio structures + * 2000-05-13: I don't remember who changed the find_format routine, + * but the change was completely broken for the Dallas + * chip. Anyway taking sampling rate into account in find_format + * is bad and should not be done unless there are devices with + * completely broken audio descriptors. Unless someone shows + * me such a descriptor, I will not allow find_format to + * take the sampling rate into account. + * Also, the former find_format made: + * - mpg123 play mono instead of stereo + * - sox completely fail for wav's with sample rates < 44.1kHz + * for the Dallas chip. + * Also fix a rather long standing problem with applications that + * use "small" writes producing no sound at all. + * 2000-05-15: My fears came true, the Philips camera indeed has pretty stupid + * audio descriptors. * */ @@ -441,9 +456,9 @@ static int dmabuf_init(struct dmabuf *db) db->bufsize = nr << PAGE_SHIFT; db->ready = 1; printk(KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " - "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d\n", + "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n", bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, - db->numfrag, db->dmasize, db->bufsize); + db->numfrag, db->dmasize, db->bufsize, db->format); return 0; } @@ -973,6 +988,8 @@ static int usbin_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; @@ -1332,6 +1349,8 @@ static int usbout_start(struct usb_audiodev *as) } spin_lock_irqsave(&as->lock, flags); } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; u->flags |= FLG_RUNNING; if (!(u->flags & FLG_URB0RUNNING)) { urb = &u->durb[0].urb; @@ -1395,30 +1414,39 @@ static int usbout_start(struct usb_audiodev *as) /* --------------------------------------------------------------------- */ -static unsigned int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int rate) +static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate) { - unsigned int i; + unsigned int g = 0; + + if (srate < afp->sratelo) + g += afp->sratelo - srate; + if (srate > afp->sratehi) + g += srate - afp->sratehi; + if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt)) + g += 0x100000; + if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt)) + g += 0x400000; + if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt)) + g += 0x100000; + if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt)) + g += 0x400000; + return g; +} + +static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate) +{ + unsigned int i, g, gb = ~0; + int j = -1; /* default to failure */ - /* first find an exact match, taking both format and sample rate into account, - but ignore stereo bit */ + /* find "best" format (according to format_goodness) */ for (i = 0; i < nr; i++) { - if (afp[i].format == (fmt & ~AFMT_STEREO) && rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; + g = format_goodness(&afp[i], fmt, srate); + if (g >= gb) + continue; + j = i; + gb = g; } - - /* second find a match with the same stereo/mono and 8bit/16bit property */ - for (i = 0; i < nr; i++) - if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) && - !AFMT_IS16BIT(afp[i].format) == !AFMT_IS16BIT(fmt) && - rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; - /* third find a match with the same number of channels */ - for (i = 0; i < nr; i++) - if (!AFMT_ISSTEREO(afp[i].format) == !AFMT_ISSTEREO(fmt) && - rate >= afp[i].sratelo && rate <= afp[i].sratehi) - return i; - /* return failure */ - return -1; + return j; } static int set_format_in(struct usb_audiodev *as) @@ -1430,9 +1458,9 @@ static int set_format_in(struct usb_audiodev *as) struct usbin *u = &as->usbin; struct dmabuf *d = &u->dma; struct audioformat *fmt; - unsigned int fmtnr, ep; + unsigned int ep; unsigned char data[3]; - int ret; + int fmtnr, ret; if (u->interface < 0 || u->interface >= config->bNumInterfaces) return 0; @@ -1465,7 +1493,9 @@ static int set_format_in(struct usb_audiodev *as) d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) d->srate = fmt->sratehi; -printk(KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) { printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", dev->devnum, u->interface, fmt->altsetting); @@ -1517,9 +1547,9 @@ static int set_format_out(struct usb_audiodev *as) struct usbout *u = &as->usbout; struct dmabuf *d = &u->dma; struct audioformat *fmt; - unsigned int fmtnr, ep; + unsigned int ep; unsigned char data[3]; - int ret; + int fmtnr, ret; if (u->interface < 0 || u->interface >= config->bNumInterfaces) return 0; @@ -1559,7 +1589,9 @@ static int set_format_out(struct usb_audiodev *as) d->srate = fmt->sratelo; if (d->srate > fmt->sratehi) d->srate = fmt->sratehi; -printk(KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#if 1 + printk(KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting); +#endif if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", dev->devnum, u->interface, fmt->altsetting); @@ -1927,6 +1959,7 @@ static int drain_out(struct usb_audiodev *as, int nonblock) if (as->usbout.dma.mapped || !as->usbout.dma.ready) return 0; + usbout_start(as); add_wait_queue(&as->usbout.dma.wait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); @@ -2033,6 +2066,7 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou ssize_t ret = 0; unsigned long flags; unsigned int ptr; + unsigned int start_thr; int cnt, err; if (ppos != &file->f_pos) @@ -2043,10 +2077,11 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; + start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES)); add_wait_queue(&as->usbout.dma.wait, &wait); while (count > 0) { #if 0 - printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%x\n", + printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n", count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, as->usbout.flags, current->state); #endif @@ -2097,7 +2132,7 @@ static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t cou count -= cnt; buffer += cnt; ret += cnt; - if (usbout_start(as)) { + if (as->usbout.dma.count >= start_thr && usbout_start(as)) { if (!ret) ret = -ENODEV; break; @@ -2525,17 +2560,6 @@ static /*const*/ struct file_operations usb_audio_fops = { /* --------------------------------------------------------------------- */ -/* - * TO DO in order to get to the point of building an OSS interface - * structure, let alone playing music.. - * - * Use kmalloc/kfree for the descriptors we build - * Write the descriptor->OSS convertor code - * Figure how we deal with mixers - * Check alternate configurations. For now assume we will find one - * zero bandwidth (idle) config and one or more live one pers interface. - */ - static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum); static void usb_audio_disconnect(struct usb_device *dev, void *ptr); @@ -2543,25 +2567,11 @@ static struct usb_driver usb_audio_driver = { "audio", usb_audio_probe, usb_audio_disconnect, - /*{ NULL, NULL }, */ LIST_HEAD_INIT(usb_audio_driver.driver_list), + LIST_HEAD_INIT(usb_audio_driver.driver_list), NULL, 0 }; - -#if 0 -static int usb_audio_irq(int state, void *buffer, int len, void *dev_id) -{ -#if 0 - struct usb_audio_device *aud = (struct usb_audio_device *)dev_id; - - printk(KERN_DEBUG "irq on %p\n", aud); -#endif - - return 1; -} -#endif - static void *find_descriptor(void *descstart, unsigned int desclen, void *after, u8 dtype, int iface, int altsetting) { @@ -3559,11 +3569,6 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr) unregister_sound_mixer(ms->dev_mixer); ms->dev_mixer = -1; } -#if 0 - if(aud->irq_handle) - usb_release_irq(dev, aud->irq_handle, aud->irqpipe); - aud->irq_handle = NULL; -#endif release(s); wake_up(&open_wait); } diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 534325986..0b7cf6b09 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -338,7 +338,7 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("portstatus %x, change %x, %s", portstatus, portchange, - portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); /* Clear the connection change status */ usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_CONNECTION); @@ -369,7 +369,7 @@ static void usb_hub_port_connect_change(struct usb_device *hub, int port) portstatus = le16_to_cpu(portsts.wPortStatus); portchange = le16_to_cpu(portsts.wPortChange); dbg("portstatus %x, change %x, %s", portstatus ,portchange, - portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "Low Speed" : "High Speed"); + portstatus&(1<<USB_PORT_FEAT_LOWSPEED) ? "1.5 Mb/s" : "12 Mb/s"); if ((portchange & USB_PORT_STAT_C_CONNECTION) || !(portstatus & USB_PORT_STAT_CONNECTION)) diff --git a/drivers/usb/input.c b/drivers/usb/input.c index 73f39db7c..35f268fac 100644 --- a/drivers/usb/input.c +++ b/drivers/usb/input.c @@ -244,6 +244,7 @@ void input_unregister_device(struct input_dev *dev) { struct input_handle *handle = dev->handle; struct input_dev **devptr = &input_dev; + struct input_handle *dnext; /* * Kill any pending repeat timers. @@ -256,9 +257,10 @@ void input_unregister_device(struct input_dev *dev) */ while (handle) { + dnext = handle->dnext; input_unlink_handle(handle); handle->handler->disconnect(handle); - handle = handle->dnext; + handle = dnext; } /* @@ -309,15 +311,17 @@ void input_unregister_handler(struct input_handler *handler) { struct input_handler **handlerptr = &input_handler; struct input_handle *handle = handler->handle; + struct input_handle *hnext; /* * Tell the handler to disconnect from all devices it keeps open. */ while (handle) { + hnext = handle->hnext; input_unlink_handle(handle); handler->disconnect(handle); - handle = handle->hnext; + handle = hnext; } /* diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index b047d0776..e97410c32 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -6,7 +6,7 @@ * Color fixes by by Orion Sky Lawlor, olawlor@acm.org, 2/26/2000 * Snapshot code by Kevin Moore * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> - * Changes by Claudio Matsuoka, claudio@conectiva.com, 3/26/2000 + * Changes by Claudio Matsuoka <claudio@conectiva.com> * * Based on the Linux CPiA driver written by Peter Pregler, * Scott J. Bertin and Johannes Erdfelt. @@ -30,7 +30,7 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -static const char version[] = "1.13"; +static const char version[] = "1.14"; #define __NO_VERSION__ @@ -64,6 +64,9 @@ static const char version[] = "1.13"; #define DEFAULT_WIDTH 640 #define DEFAULT_HEIGHT 480 +#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_RGB24 ? 384 : 256) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_RGB24 ? 24 : 8) + /* PARAMETER VARIABLES: */ static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ @@ -90,9 +93,13 @@ static int sensor = 0; static int i2c_detect_tries = 5; /* For legal values, see the OV7610/7620 specs under register Common F, - upper nybble (set to 0-F) */ + * upper nybble (set to 0-F) */ static int aperture = -1; +/* Force image to be read in RGB instead of BGR. This option allow + * programs that expect RGB data (e.g. gqcam) to work with this driver. */ +static int force_rgb = 0; + MODULE_PARM(autoadjust, "i"); MODULE_PARM(debug, "i"); MODULE_PARM(fix_rgb_offset, "i"); @@ -100,8 +107,9 @@ MODULE_PARM(snapshot, "i"); MODULE_PARM(sensor, "i"); MODULE_PARM(i2c_detect_tries, "i"); MODULE_PARM(aperture, "i"); +MODULE_PARM(force_rgb, "i"); -MODULE_AUTHOR("Mark McClelland (and others)"); +MODULE_AUTHOR("Mark McClelland <mmcclelland@delphi.com> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"); MODULE_DESCRIPTION("OV511 USB Camera Driver"); char kernel_version[] = UTS_RELEASE; @@ -126,6 +134,26 @@ static struct cam_list clist[] = { { -1, NULL } }; +static struct palette_list plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P,"YUV422P" }, + { VIDEO_PALETTE_YUV411P,"YUV411P" }, + { VIDEO_PALETTE_YUV420P,"YUV420P" }, + { VIDEO_PALETTE_YUV410P,"YUV410P" }, + { -1, NULL } +}; + /********************************************************************** * * Memory management @@ -236,7 +264,8 @@ static void rvfree(void *mem, unsigned long size) **********************************************************************/ #ifdef CONFIG_PROC_FS -static struct proc_dir_entry *ov511_proc_root = NULL; +static struct proc_dir_entry *ov511_proc_entry = NULL; +static struct proc_dir_entry *video_proc_entry = NULL; #define YES_NO(x) ((x) ? "yes" : "no") @@ -244,7 +273,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; - int i, len; + int i, j, len; struct usb_ov511 *ov511 = data; /* IMPORTANT: This output MUST be kept under PAGE_SIZE @@ -259,6 +288,10 @@ static int ov511_read_proc(char *page, char **start, off_t off, out += sprintf (out, "subcapture : %s\n", YES_NO (ov511->sub_flag)); out += sprintf (out, "sub_size : %d %d %d %d\n", ov511->subx, ov511->suby, ov511->subw, ov511->subh); + out += sprintf (out, "data_format : %s\n", force_rgb ? "RGB" : "BGR"); + out += sprintf (out, "brightness : %d\n", ov511->brightness >> 8); + out += sprintf (out, "colour : %d\n", ov511->colour >> 8); + out += sprintf (out, "contrast : %d\n", ov511->contrast >> 8); out += sprintf (out, "num_frames : %d\n", OV511_NUMFRAMES); for (i = 0; i < OV511_NUMFRAMES; i++) { out += sprintf (out, "frame : %d\n", i); @@ -266,27 +299,40 @@ static int ov511_read_proc(char *page, char **start, off_t off, ov511->frame[i].depth); out += sprintf (out, " size : %d %d\n", ov511->frame[i].width, ov511->frame[i].height); +#if 0 out += sprintf (out, " hdr_size : %d %d\n", ov511->frame[i].hdrwidth, ov511->frame[i].hdrheight); - out += sprintf (out, " format : %d\n", - ov511->frame[i].format); +#endif + out += sprintf (out, " format : "); + for (j = 0; plist[j].num >= 0; j++) { + if (plist[j].num == ov511->frame[i].format) { + out += sprintf (out, "%s\n", plist[j].name); + break; + } + } + if (plist[j].num < 0) + out += sprintf (out, "unknown\n"); out += sprintf (out, " segsize : %d\n", ov511->frame[i].segsize); + out += sprintf (out, " data_buffer : 0x%p\n", + ov511->frame[i].data); #if 0 - out += sprintf (out, " curline : %d\n", - ov511->frame[i].curline); - out += sprintf (out, " segment : %d\n", - ov511->frame[i].segment); - out += sprintf (out, " scanlength : %ld\n", - ov511->frame[i].scanlength); out += sprintf (out, " bytesread : %ld\n", ov511->frame[i].bytes_read); #endif } out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled)); - out += sprintf (out, "bridge : %d\n", ov511->bridge); - out += sprintf (out, "sensor : %d\n", ov511->sensor); + out += sprintf (out, "bridge : %s\n", + ov511->bridge == BRG_OV511 ? "OV511" : + ov511->bridge == BRG_OV511PLUS ? "OV511+" : + "unknown"); + out += sprintf (out, "sensor : %s\n", + ov511->sensor == SEN_OV7610 ? "OV7610" : + ov511->sensor == SEN_OV7620 ? "OV7620" : + ov511->sensor == SEN_OV7620AE ? "OV7620AE" : + "unknown"); out += sprintf (out, "packet_size : %d\n", ov511->packet_size); + out += sprintf (out, "framebuffer : 0x%p\n", ov511->fbuf); len = out - page; len -= off; @@ -303,130 +349,7 @@ static int ov511_read_proc(char *page, char **start, off_t off, static int ov511_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { - int retval = -EINVAL; - -#if 0 - /* struct cam_data *cam = data; */ - struct usb_ov511 new_params; - int size = count; - int find_colon; - unsigned long val; - u32 command_flags = 0; - u8 new_mains; - - if (down_interruptible(&cam->param_lock)) - return -ERESTARTSYS; - - /* - * Skip over leading whitespace - */ - while (count && isspace(*buffer)) { - --count; - ++buffer; - } - - -#define MATCH(x) \ - ({ \ - int _len = strlen(x), _ret, _colon_found; \ - _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \ - if (_ret) { \ - buffer += _len; \ - count -= _len; \ - if (find_colon) { \ - _colon_found = 0; \ - while (count && (*buffer == ' ' || *buffer == '\t' || \ - (!_colon_found && *buffer == ':'))) { \ - if (*buffer == ':') \ - _colon_found = 1; \ - --count; \ - ++buffer; \ - } \ - if (!count || !_colon_found) \ - retval = -EINVAL; \ - find_colon = 0; \ - } \ - } \ - _ret; \ - }) - -#define VALUE \ - ({ \ - char *_p; \ - unsigned long int _ret; \ - _ret = simple_strtoul(buffer, &_p, 0); \ - if (_p == buffer) \ - retval = -EINVAL; \ - else { \ - count -= _p - buffer; \ - buffer = _p; \ - } \ - _ret; \ - }) - - - retval = 0; - while (count && !retval) { - find_colon = 1; - - if (MATCH("")) { - if (!retval) - val = VALUE; - - if (!retval) { - if (val <= 0xff) - /* ... = val */ ; - else - retval = -EINVAL; - } - } else { - DBG("No match found\n"); - retval = -EINVAL; - } - - if (!retval) { - while (count && isspace(*buffer) && *buffer != '\n') { - --count; - ++buffer; - } - if (count) { - if (*buffer != '\n' && *buffer != ';') - retval = -EINVAL; - else { - --count; - ++buffer; - } - } - } - } - -#undef MATCH -#undef FIRMWARE_VERSION -#undef VALUE -#undef FIND_VALUE -#undef FIND_END - if (!retval) { - if (command_flags & COMMAND_SETCOLOURPARAMS) { - /* Adjust cam->vp to reflect these changes */ - cam->vp.brightness = - new_params.colourParams.brightness*65535/100; - cam->vp.contrast = - new_params.colourParams.contrast*65535/100; - cam->vp.colour = - new_params.colourParams.saturation*65535/100; - } - - memcpy(&cam->params, &new_params, sizeof(struct cam_params)); - cam->mainsFreq = new_mains; - cam->cmd_queue |= command_flags; - retval = size; - } else - PDEBUG(3, "error: %d\n", retval); - - up(&cam->param_lock); -#endif - - return retval; + return -EINVAL; } static void create_proc_ov511_cam (struct usb_ov511 *ov511) @@ -434,14 +357,15 @@ static void create_proc_ov511_cam (struct usb_ov511 *ov511) char name[7]; struct proc_dir_entry *ent; - PDEBUG (4, "***************"); - if (!ov511_proc_root || !ov511) + PDEBUG (4, "creating /proc/video/ov511/videoX entry"); + if (!ov511_proc_entry || !ov511) return; sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "==== name: %s", name); + PDEBUG (4, "creating %s", name); - ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_root); + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry); + if (!ent) return; @@ -460,26 +384,44 @@ static void destroy_proc_ov511_cam (struct usb_ov511 *ov511) return; sprintf(name, "video%d", ov511->vdev.minor); - PDEBUG (4, "==== name: %s", name); -#if 0 - remove_proc_entry(name, ov511_proc_root); + PDEBUG (4, "destroying %s", name); + remove_proc_entry(name, ov511_proc_entry); ov511->proc_entry = NULL; -#endif } static void proc_ov511_create(void) { - ov511_proc_root = create_proc_entry("ov511", S_IFDIR, 0); + struct proc_dir_entry *p = NULL; + + /* No current standard here. Alan prefers /proc/video/ as it keeps + * /proc "less cluttered than /proc/randomcardifoundintheshed/" + * -claudio + */ + PDEBUG (3, "creating /proc/video"); + video_proc_entry = proc_mkdir("video", p); + if (!video_proc_entry) { + if (!p) { + err("Unable to initialise /proc/video\n"); + return; + } else { /* FIXME - this doesn't work */ + PDEBUG (3, "/proc/video already exists"); + video_proc_entry = p; + } + } - if (ov511_proc_root) - ov511_proc_root->owner = THIS_MODULE; + ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry); + + if (ov511_proc_entry) + ov511_proc_entry->owner = THIS_MODULE; else - printk("Unable to initialise /proc/ov511\n"); /***********/ + err("Unable to initialise /proc/video/ov511\n"); } static void proc_ov511_destroy(void) { - remove_proc_entry("ov511", 0); + PDEBUG (3, "removing /proc/video/ov511"); + remove_proc_entry("ov511", video_proc_entry); + remove_proc_entry("video", NULL); } #endif /* CONFIG_PROC_FS */ @@ -510,7 +452,6 @@ static int ov511_reg_write(struct usb_device *dev, return rc; } - /* returns: negative is error, pos or zero is data */ static int ov511_reg_read(struct usb_device *dev, unsigned char reg) { @@ -533,7 +474,6 @@ static int ov511_reg_read(struct usb_device *dev, unsigned char reg) } } - static int ov511_i2c_write(struct usb_device *dev, unsigned char reg, unsigned char value) @@ -580,7 +520,6 @@ error: return rc; } - /* returns: negative is error, pos or zero is data */ static int ov511_i2c_read(struct usb_device *dev, unsigned char reg) { @@ -653,7 +592,6 @@ error: return rc; } - static int ov511_write_regvals(struct usb_device *dev, struct ov511_regvals * pRegvals) { @@ -682,9 +620,8 @@ error: return rc; } - -#if 0 -static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) +#ifdef OV511_DEBUG +static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn) { int i; int rc; @@ -694,15 +631,13 @@ static void ov511_dump_i2c_range( struct usb_device *dev, int reg1, int regn) } } - -static void ov511_dump_i2c_regs( struct usb_device *dev) +static void ov511_dump_i2c_regs(struct usb_device *dev) { PDEBUG(3, "I2C REGS"); ov511_dump_i2c_range(dev, 0x00, 0x38); } - -static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn) +static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) { int i; int rc; @@ -712,8 +647,8 @@ static void ov511_dump_reg_range( struct usb_device *dev, int reg1, int regn) } } - -static void ov511_dump_regs( struct usb_device *dev) +#if 0 +static void ov511_dump_regs(struct usb_device *dev) { PDEBUG(1, "CAMERA INTERFACE REGS"); ov511_dump_reg_range(dev, 0x10, 0x1f); @@ -736,7 +671,7 @@ static void ov511_dump_regs( struct usb_device *dev) } #endif - +#endif static int ov511_reset(struct usb_device *dev, unsigned char reset_type) { @@ -752,24 +687,21 @@ static int ov511_reset(struct usb_device *dev, unsigned char reset_type) return rc; } - /* Temporarily stops OV511 from functioning. Must do this before changing * registers while the camera is streaming */ static inline int ov511_stop(struct usb_device *dev) { - PDEBUG(4, "ov511_stop()"); + PDEBUG(4, "stopping"); return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d)); } - /* Restarts OV511 after ov511_stop() is called */ static inline int ov511_restart(struct usb_device *dev) { - PDEBUG(4, "ov511_restart()"); + PDEBUG(4, "restarting"); return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00)); } - static int ov511_set_packet_size(struct usb_ov511 *ov511, int size) { int alt, mult; @@ -844,9 +776,16 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) if (ov511_stop(dev) < 0) return -EIO; + ov511->contrast = p->contrast; + ov511->brightness = p->brightness; + ov511->colour = p->colour; + ov511->hue = p->hue; + ov511->whiteness = p->whiteness; + if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0) return -EIO; #if 0 + /* disable auto adjust mode */ if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0) return -EIO; #endif @@ -880,7 +819,6 @@ ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } - static inline int ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) { @@ -903,12 +841,10 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) p->hue = 0x8000; p->whiteness = 105 << 8; -#if 0 - p->depth = 3; /* Don't know if this is right */ -#else - p->depth = 24; -#endif - p->palette = VIDEO_PALETTE_RGB24; + + /* Can we get these from frame[0]? -claudio? */ + p->depth = ov511->frame[0].depth; + p->palette = ov511->frame[0].format; if (ov511_restart(dev) < 0) return -EIO; @@ -916,6 +852,22 @@ ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) return 0; } +/* FIXME: add 176x144, 160x140 */ +static struct mode_list mlist[] = { + { 640, 480, VIDEO_PALETTE_GREY, 0x4f, 0x3d, 0x00, 0x00, + 0x4f, 0x3d, 0x00, 0x00, 0x04, 0x03, 0x24, 0x04, 0x9e }, + { 640, 480, VIDEO_PALETTE_RGB24,0x4f, 0x3d, 0x00, 0x00, + 0x4f, 0x3d, 0x00, 0x00, 0x06, 0x03, 0x24, 0x04, 0x9e }, + { 320, 240, VIDEO_PALETTE_GREY, 0x27, 0x1f, 0x00, 0x00, + 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, + { 320, 240, VIDEO_PALETTE_RGB24,0x27, 0x1f, 0x00, 0x00, + 0x27, 0x1f, 0x00, 0x00, 0x01, 0x03, 0x04, 0x24, 0x1e }, + { 352, 288, VIDEO_PALETTE_GREY, 0x2b, 0x25, 0x00, 0x00, + 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 352, 288, VIDEO_PALETTE_RGB24,0x2b, 0x25, 0x00, 0x00, + 0x2b, 0x25, 0x00, 0x00, 0x01, 0x03, 0x04, 0x04, 0x1e }, + { 0, 0 } +}; static int ov511_mode_init_regs(struct usb_ov511 *ov511, @@ -925,6 +877,7 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, struct usb_device *dev = ov511->dev; int hwsbase = 0; int hwebase = 0; + int i; PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", width, height, mode, sub_flag); @@ -975,6 +928,9 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, break; } +#if 0 + /* FIXME: subwindow support is currently broken! + */ if (width == 640 && height == 480) { if (sub_flag) { /* horizontal window start */ @@ -1018,41 +974,51 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, } } - ov511_reg_write(dev, 0x14, 0x00); - ov511_reg_write(dev, 0x15, 0x00); + ov511_reg_write(dev, 0x14, 0x00); /* Pixel divisor */ + ov511_reg_write(dev, 0x15, 0x00); /* Line divisor */ /* FIXME?? Shouldn't below be true only for YUV420? */ - ov511_reg_write(dev, 0x18, 0x03); + ov511_reg_write(dev, 0x18, 0x03); /* YUV420/422, YFIR */ - ov511_i2c_write(dev, 0x12, 0x24); - ov511_i2c_write(dev, 0x14, 0x04); + ov511_i2c_write(dev, 0x12, 0x24); /* Common A */ + ov511_i2c_write(dev, 0x14, 0x04); /* Common C */ /* 7620 doesn't have register 0x35, so play it safe */ if (ov511->sensor != SEN_OV7620) ov511_i2c_write(dev, 0x35, 0x9e); - } else if (width == 320 && height == 240) { - ov511_reg_write(dev, 0x12, 0x27); - ov511_reg_write(dev, 0x13, 0x1f); - ov511_reg_write(dev, 0x14, 0x00); - ov511_reg_write(dev, 0x15, 0x00); - ov511_reg_write(dev, 0x18, 0x03); +#endif + + for (i = 0; mlist[i].width; i++) { + if (width != mlist[i].width || + height != mlist[i].height || + mode != mlist[i].mode) + continue; + + ov511_reg_write(dev, 0x12, mlist[i].pxcnt); + ov511_reg_write(dev, 0x13, mlist[i].lncnt); + ov511_reg_write(dev, 0x14, mlist[i].pxdv); + ov511_reg_write(dev, 0x15, mlist[i].lndv); + ov511_reg_write(dev, 0x18, mlist[i].m420); /* Snapshot additions */ - ov511_reg_write(dev, 0x1a, 0x27); - ov511_reg_write(dev, 0x1b, 0x1f); - ov511_reg_write(dev, 0x1c, 0x00); - ov511_reg_write(dev, 0x1d, 0x00); + ov511_reg_write(dev, 0x1a, mlist[i].s_pxcnt); + ov511_reg_write(dev, 0x1b, mlist[i].s_lncnt); + ov511_reg_write(dev, 0x1c, mlist[i].s_pxdv); + ov511_reg_write(dev, 0x1d, mlist[i].s_lndv); - if (mode == VIDEO_PALETTE_GREY) { - ov511_i2c_write(dev, 0x11, 1); /* check */ - } else { - ov511_i2c_write(dev, 0x11, 1); /* check */ - } + ov511_i2c_write(dev, 0x11, mlist[i].clock); /* check */ - ov511_i2c_write(dev, 0x12, 0x04); - ov511_i2c_write(dev, 0x14, 0x24); - ov511_i2c_write(dev, 0x35, 0x1e); - } else { + ov511_i2c_write(dev, 0x12, mlist[i].common_A); + ov511_i2c_write(dev, 0x14, mlist[i].common_C); + + /* 7620 doesn't have register 0x35, so play it safe */ + if (ov511->sensor != SEN_OV7620) + ov511_i2c_write(dev, 0x35, mlist[i].common_L); + + break; + } + + if (mlist[i].width == 0) { err("Unknown mode (%d, %d): %d", width, height, mode); rc = -EINVAL; } @@ -1060,10 +1026,14 @@ ov511_mode_init_regs(struct usb_ov511 *ov511, if (ov511_restart(ov511->dev) < 0) return -EIO; +#ifdef OV511_DEBUG + if (debug >= 5) + ov511_dump_i2c_regs(dev); +#endif + return rc; } - /********************************************************************** * * Color correction functions @@ -1093,32 +1063,38 @@ static inline void ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, unsigned char * rgb) { - const double brightness = 1.0; // 0->black; 1->full scale - const double saturation = 1.0; // 0->greyscale; 1->full color + const double brightness = 1.0; /* 0->black; 1->full scale */ + const double saturation = 1.0; /* 0->greyscale; 1->full color */ const double fixScale = brightness * 256 * 256; const int rvScale = (int)(1.402 * saturation * fixScale); const int guScale = (int)(-0.344136 * saturation * fixScale); const int gvScale = (int)(-0.714136 * saturation * fixScale); const int buScale = (int)(1.772 * saturation * fixScale); const int yScale = (int)(fixScale); + int r, g, b; + + g = guScale * u + gvScale * v; + if (force_rgb) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } - int r = rvScale * v; - int g = guScale * u + gvScale * v; - int b = buScale * u; yTL *= yScale; yTR *= yScale; yBL *= yScale; yBR *= yScale; - //Write out top two pixels + /* Write out top two pixels */ rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); - //Skip down to next line to write out bottom two pixels + /* Skip down to next line to write out bottom two pixels */ rgb += 3 * rowPixels; rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); } - /* * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. * The first 64 bytes of each segment are U, the next 64 are V. The U and @@ -1155,13 +1131,13 @@ ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, #undef OV511_DUMPPIX static void -ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, +ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iOutUV, int iHalf, int iWidth) { #ifndef OV511_DUMPPIX int k, l, m; - unsigned char * pIn; - unsigned char * pOut, * pOut1; + unsigned char *pIn; + unsigned char *pOut, *pOut1; /* Just copy the Y's if in the first stripe */ if (!iHalf) { @@ -1232,7 +1208,6 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, pOut += 8 * 3; } } - #else /* Just dump pix data straight out for debug */ int i, j; @@ -1249,7 +1224,6 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, #endif } - /* * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. * The segments represent 4 squares of 8x8 pixels as follows: @@ -1257,11 +1231,11 @@ ov511_parse_data_rgb24(unsigned char * pIn0, unsigned char * pOut0, * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 * 8 9 ... 15 72 73 ... 79 200 201 ... 207 * ... ... ... - * 56 57 ... 63 120 121 127 248 249 ... 255 + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 * */ static void -ov511_parse_data_grey(unsigned char * pIn0, unsigned char * pOut0, +ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, int iOutY, int iWidth) { int k, l, m; @@ -1276,13 +1250,12 @@ ov511_parse_data_grey(unsigned char * pIn0, unsigned char * pOut0, for (m = 0; m < 8; m++) { *pOut1++ = *pIn++; } - pOut1 += iWidth - 8; + pOut1 += iWidth - WDIV; } pOut += 8; } } - /* * fixFrameRGBoffset-- * My camera seems to return the red channel about 1 pixel @@ -1317,7 +1290,6 @@ static void fixFrameRGBoffset(struct ov511_frame *frame) } } - /********************************************************************** * * OV511 data transfer, IRQ handler @@ -1488,7 +1460,6 @@ static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) return totlen; } - static void ov511_isoc_irq(struct urb *urb) { int len; @@ -1520,7 +1491,6 @@ static void ov511_isoc_irq(struct urb *urb) return; } - static int ov511_init_isoc(struct usb_ov511 *ov511) { urb_t *urb; @@ -1595,7 +1565,6 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) return 0; } - static void ov511_stop_isoc(struct usb_ov511 *ov511) { if (!ov511->streaming || !ov511->dev) @@ -1622,7 +1591,6 @@ static void ov511_stop_isoc(struct usb_ov511 *ov511) } } - static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) { struct ov511_frame *frame; @@ -1658,25 +1626,17 @@ static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) /* Make sure it's not too big */ if (width > DEFAULT_WIDTH) width = DEFAULT_WIDTH; -#if 0 - width = (width / 8) * 8; /* Multiple of 8 */ -#endif - width &= ~7L; + + width &= ~7L; /* Multiple of 8 */ if (height > DEFAULT_HEIGHT) height = DEFAULT_HEIGHT; -#if 0 - height = (height / 4) * 4; /* Multiple of 4 */ -#endif - width &= ~3L; -// /* We want a fresh frame every 30 we get */ -// ov511->compress = (ov511->compress + 1) % 30; + width &= ~3L; /* Multiple of 4 */ return 0; } - /**************************************************************************** * * V4L API @@ -1687,43 +1647,45 @@ static int ov511_open(struct video_device *dev, int flags) { int err = -EBUSY; struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; - PDEBUG(4, "ov511_open"); + PDEBUG(4, "opening"); down(&ov511->lock); - if (ov511->user) - goto out_unlock; - ov511->frame[0].grabstate = FRAME_UNUSED; - ov511->frame[1].grabstate = FRAME_UNUSED; + if (ov511->user) { + up(&ov511->lock); + return -EBUSY; + } err = -ENOMEM; /* Allocate memory for the frame buffers */ - ov511->fbuf = rvmalloc(2 * MAX_DATA_SIZE); + ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE); if (!ov511->fbuf) - goto open_err_ret; + return err; - ov511->frame[0].data = ov511->fbuf; - ov511->frame[1].data = ov511->fbuf + MAX_DATA_SIZE; ov511->sub_flag = 0; - PDEBUG(4, "frame [0] @ %p", ov511->frame[0].data); - PDEBUG(4, "frame [1] @ %p", ov511->frame[1].data); + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].grabstate = FRAME_UNUSED; + ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; + PDEBUG(4, "frame [%d] @ %p", i, ov511->frame[0].data); - ov511->sbuf[0].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[0].data) - goto open_err_on0; - ov511->sbuf[1].data = kmalloc(FRAMES_PER_DESC * MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); - if (!ov511->sbuf[1].data) - goto open_err_on1; - - PDEBUG(4, "sbuf[0] @ %p", ov511->sbuf[0].data); - PDEBUG(4, "sbuf[1] @ %p", ov511->sbuf[1].data); + ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov511->sbuf[i].data) { +open_free_ret: + while (--i) kfree(ov511->sbuf[i].data); + rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); + return err; + } + PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); + } err = ov511_init_isoc(ov511); if (err) - goto open_err_on2; + goto open_free_ret; ov511->user++; up(&ov511->lock); @@ -1731,24 +1693,12 @@ static int ov511_open(struct video_device *dev, int flags) MOD_INC_USE_COUNT; return 0; - -open_err_on2: - kfree (ov511->sbuf[1].data); -open_err_on1: - kfree (ov511->sbuf[0].data); -open_err_on0: - rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); -open_err_ret: - return err; -out_unlock: - up(&ov511->lock); - return err; } - static void ov511_close(struct video_device *dev) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; PDEBUG(4, "ov511_close"); @@ -1759,10 +1709,9 @@ static void ov511_close(struct video_device *dev) ov511_stop_isoc(ov511); - rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); - - kfree(ov511->sbuf[1].data); - kfree(ov511->sbuf[0].data); + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + for (i = 0; i < OV511_NUMFRAMES; i++) + kfree(ov511->sbuf[i].data); up(&ov511->lock); @@ -1772,7 +1721,6 @@ static void ov511_close(struct video_device *dev) } } - static int ov511_init_done(struct video_device *dev) { #ifdef CONFIG_PROC_FS @@ -1782,13 +1730,11 @@ static int ov511_init_done(struct video_device *dev) return 0; } - static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) { return -EINVAL; } - static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; @@ -2097,7 +2043,6 @@ redo: return 0; } - static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock) { struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; @@ -2138,10 +2083,6 @@ static long ov511_read(struct video_device *dev, char *buf, unsigned long count, frame = &ov511->frame[frmx]; - /* FIXME */ - frame->segsize = frame->format == VIDEO_PALETTE_RGB24 ? 384 : 256; - frame->depth = frame->format == VIDEO_PALETTE_RGB24 ? 24 : 8; - restart: if (!ov511->dev) return -EIO; @@ -2165,11 +2106,8 @@ restart: /* Repeat until we get a snapshot frame */ - if (!ov511->snap_enabled) { - PDEBUG (4, "snap disabled"); - } else { + if (ov511->snap_enabled) PDEBUG (4, "Waiting snapshot frame"); - } if (ov511->snap_enabled && !frame->snapshot) { frame->bytes_read = 0; if (ov511_new_frame(ov511, frmx)) @@ -2178,13 +2116,11 @@ restart: } /* Clear the snapshot */ - if (ov511->snap_enabled) - PDEBUG (4, "Clear snapshot"); if (ov511->snap_enabled && frame->snapshot) { frame->snapshot = 0; - ov511_reg_write(ov511->dev, 0x52, 0x01); - ov511_reg_write(ov511->dev, 0x52, 0x03); - ov511_reg_write(ov511->dev, 0x52, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); } PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, @@ -2212,7 +2148,7 @@ restart: /* Mark it as available to be used again. */ ov511->frame[frmx].grabstate = FRAME_UNUSED; - if (ov511_new_frame(ov511, frmx ? 0 : 1)) + if (ov511_new_frame(ov511, !frmx)) err("ov511_new_frame returned error"); } @@ -2221,7 +2157,6 @@ restart: return count; } - static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long size) { @@ -2229,7 +2164,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, unsigned long start = (unsigned long)adr; unsigned long page, pos; - if (!ov511->dev) + if (ov511->dev == NULL) return -EIO; PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); @@ -2238,8 +2173,7 @@ static int ov511_mmap(struct video_device *dev, const char *adr, return -EINVAL; pos = (unsigned long)ov511->fbuf; - while (size > 0) - { + while (size > 0) { page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; @@ -2254,7 +2188,6 @@ static int ov511_mmap(struct video_device *dev, const char *adr, return 0; } - static struct video_device ov511_template = { name: "OV511 USB Camera", type: VID_TYPE_CAPTURE, @@ -2268,7 +2201,6 @@ static struct video_device ov511_template = { initialize: ov511_init_done, }; - /**************************************************************************** * * OV511/OV7610 configuration @@ -2281,68 +2213,70 @@ static int ov76xx_configure(struct usb_ov511 *ov511) int i, success; int rc; - static struct ov511_regvals aRegvalsNorm7610[] = - {{OV511_I2C_BUS, 0x10, 0xff}, - {OV511_I2C_BUS, 0x16, 0x06}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x06, 0x00}, - {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x38, 0x81}, - {OV511_I2C_BUS, 0x28, 0x24}, /* 0c */ - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x0f, 0x05}, - {OV511_I2C_BUS, 0x15, 0x01}, - {OV511_I2C_BUS, 0x20, 0x1c}, - {OV511_I2C_BUS, 0x23, 0x2a}, - {OV511_I2C_BUS, 0x24, 0x10}, - {OV511_I2C_BUS, 0x25, 0x8a}, - {OV511_I2C_BUS, 0x27, 0xc2}, - {OV511_I2C_BUS, 0x29, 0x03}, /* 91 */ - {OV511_I2C_BUS, 0x2a, 0x04}, - {OV511_I2C_BUS, 0x2c, 0xfe}, - {OV511_I2C_BUS, 0x30, 0x71}, - {OV511_I2C_BUS, 0x31, 0x60}, - {OV511_I2C_BUS, 0x32, 0x26}, - {OV511_I2C_BUS, 0x33, 0x20}, - {OV511_I2C_BUS, 0x34, 0x48}, - {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x11, 0x01}, - {OV511_I2C_BUS, 0x0c, 0x24}, - {OV511_I2C_BUS, 0x0d, 0x24}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm7610[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x06, 0x00 }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x38, 0x81 }, + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x20, 0x1c }, + { OV511_I2C_BUS, 0x23, 0x2a }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xc2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ + { OV511_I2C_BUS, 0x2a, 0x04 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; - static struct ov511_regvals aRegvalsNorm7620[] = - {{OV511_I2C_BUS, 0x10, 0xff}, - {OV511_I2C_BUS, 0x16, 0x06}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x2b, 0xac}, - {OV511_I2C_BUS, 0x12, 0x00}, - {OV511_I2C_BUS, 0x28, 0x24}, - {OV511_I2C_BUS, 0x05, 0x00}, - {OV511_I2C_BUS, 0x0f, 0x05}, - {OV511_I2C_BUS, 0x15, 0x01}, - {OV511_I2C_BUS, 0x23, 0x00}, - {OV511_I2C_BUS, 0x24, 0x10}, - {OV511_I2C_BUS, 0x25, 0x8a}, - {OV511_I2C_BUS, 0x27, 0xe2}, - {OV511_I2C_BUS, 0x29, 0x03}, - {OV511_I2C_BUS, 0x2a, 0x00}, - {OV511_I2C_BUS, 0x2c, 0xfe}, - {OV511_I2C_BUS, 0x30, 0x71}, - {OV511_I2C_BUS, 0x31, 0x60}, - {OV511_I2C_BUS, 0x32, 0x26}, - {OV511_I2C_BUS, 0x33, 0x20}, - {OV511_I2C_BUS, 0x34, 0x48}, - {OV511_I2C_BUS, 0x12, 0x24}, - {OV511_I2C_BUS, 0x11, 0x01}, - {OV511_I2C_BUS, 0x0c, 0x24}, - {OV511_I2C_BUS, 0x0d, 0x24}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm7620[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x05 }, + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xe2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, + { OV511_I2C_BUS, 0x2a, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; + PDEBUG (4, "starting configuration"); + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, OV7610_I2C_WRITE_ID) < 0) return -1; @@ -2385,13 +2319,13 @@ static int ov76xx_configure(struct usb_ov511 *ov511) err("Error detecting sensor type"); return -1; } else if((rc & 3) == 3) { - printk("ov511: Sensor is an OV7610\n"); + info("Sensor is an OV7610"); ov511->sensor = SEN_OV7610; } else if((rc & 3) == 1) { - printk("ov511: Sensor is an OV7620AE\n"); + info("Sensor is an OV7620AE"); ov511->sensor = SEN_OV7620AE; } else if((rc & 3) == 0) { - printk("ov511: Sensor is an OV7620\n"); + info("Sensor is an OV7620"); ov511->sensor = SEN_OV7620; } else { err("Unknown image sensor version: %d", rc & 3); @@ -2399,13 +2333,15 @@ static int ov76xx_configure(struct usb_ov511 *ov511) } } else { /* sensor != 0; user overrode detection */ ov511->sensor = sensor; - printk("ov511: Sensor set to type %d\n", ov511->sensor); + info("Sensor set to type %d", ov511->sensor); } if (ov511->sensor == SEN_OV7620) { + PDEBUG(4, "Writing 7620 registers"); if (ov511_write_regvals(dev, aRegvalsNorm7620)) return -1; } else { + PDEBUG(4, "Writing 7610 registers"); if (ov511_write_regvals(dev, aRegvalsNorm7610)) return -1; } @@ -2438,34 +2374,35 @@ static int ov76xx_configure(struct usb_ov511 *ov511) static int ov511_configure(struct usb_ov511 *ov511) { struct usb_device *dev = ov511->dev; + int i; - static struct ov511_regvals aRegvalsInit[] = - {{OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f}, - {OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01}, - {OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsInit[] = { + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d }, + { OV511_DONE_BUS, 0x0, 0x00}, }; - static struct ov511_regvals aRegvalsNorm511[] = - {{OV511_REG_BUS, 0x20, 0x01}, - {OV511_REG_BUS, 0x52, 0x02}, - {OV511_REG_BUS, 0x52, 0x00}, - {OV511_REG_BUS, 0x31, 0x1f}, /* 0f */ - {OV511_REG_BUS, 0x70, 0x3f}, - {OV511_REG_BUS, 0x71, 0x3f}, - {OV511_REG_BUS, 0x72, 0x01}, - {OV511_REG_BUS, 0x73, 0x01}, - {OV511_REG_BUS, 0x74, 0x01}, - {OV511_REG_BUS, 0x75, 0x01}, - {OV511_REG_BUS, 0x76, 0x01}, - {OV511_REG_BUS, 0x77, 0x01}, - {OV511_REG_BUS, 0x78, 0x06}, - {OV511_REG_BUS, 0x79, 0x03}, - {OV511_DONE_BUS, 0x0, 0x00}, + static struct ov511_regvals aRegvalsNorm511[] = { + { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */ + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 }, + { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, }; memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); @@ -2488,16 +2425,15 @@ static int ov511_configure(struct usb_ov511 *ov511) /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used * (using read() instead). */ - ov511->frame[0].width = DEFAULT_WIDTH; - ov511->frame[0].height = DEFAULT_HEIGHT; - ov511->frame[0].depth = 24; /**************/ - ov511->frame[0].bytes_read = 0; - ov511->frame[0].segment = 0; - ov511->frame[1].width = DEFAULT_WIDTH; - ov511->frame[1].height = DEFAULT_HEIGHT; - ov511->frame[1].depth = 24; - ov511->frame[1].bytes_read = 0; - ov511->frame[1].segment = 0; + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = DEFAULT_WIDTH; + ov511->frame[i].height = DEFAULT_HEIGHT; + ov511->frame[i].depth = 24; + ov511->frame[i].bytes_read = 0; + ov511->frame[i].segment = 0; + ov511->frame[i].format = VIDEO_PALETTE_RGB24; + ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); + } /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ @@ -2588,7 +2524,7 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) PDEBUG (4, "CustomID = %d", ov511->customid); for (i = 0; clist[i].id >= 0; i++) { if (ov511->customid == clist[i].id) { - printk ("Camera: %s\n", clist[i].description); + info("camera: %s", clist[i].description); ov511->desc = i; break; } @@ -2606,6 +2542,11 @@ static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) err("support for your camera."); } + /* Workaround for some applications that want data in RGB + * instead of BGR */ + if (force_rgb) + info("data format set to RGB"); + if (!ov511_configure(ov511)) { ov511->user = 0; init_MUTEX(&ov511->lock); /* to 1 == available */ @@ -2670,7 +2611,6 @@ static void ov511_disconnect(struct usb_device *dev, void *ptr) } #ifdef CONFIG_PROC_FS - PDEBUG(3, "destroying /proc/ov511/video%d", ov511->vdev.minor); destroy_proc_ov511_cam(ov511); #endif @@ -2712,13 +2652,12 @@ static int __init usb_ov511_init(void) static void __exit usb_ov511_exit(void) { + usb_deregister(&ov511_driver); + info("ov511 driver deregistered"); + #ifdef CONFIG_PROC_FS - PDEBUG(3, "destroying /proc/ov511"); proc_ov511_destroy(); #endif - - usb_deregister(&ov511_driver); - info("ov511 driver deregistered"); } module_init(usb_ov511_init); diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h index 5b8ce967d..d3a9a78ba 100644 --- a/drivers/usb/ov511.h +++ b/drivers/usb/ov511.h @@ -1,3 +1,4 @@ + #ifndef __LINUX_OV511_H #define __LINUX_OV511_H @@ -9,7 +10,7 @@ #ifdef OV511_DEBUG # define PDEBUG(level, fmt, args...) \ -if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LINE__ , ## args) +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) #else # define PDEBUG(level, fmt, args...) do {} while(0) #endif @@ -119,48 +120,55 @@ if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LI #define OV511PLUS_ALT_SIZE_961 7 /* OV7610 registers */ -#define OV7610_REG_GAIN 0x00 -#define OV7610_REG_BLUE 0x01 -#define OV7610_REG_RED 0x02 -#define OV7610_REG_SAT 0x03 -#define OV7610_REG_CNT 0x05 -#define OV7610_REG_BRT 0x06 -#define OV7610_REG_BLUE_BIAS 0x0C -#define OV7610_REG_RED_BIAS 0x0D -#define OV7610_REG_GAMMA_COEFF 0x0E -#define OV7610_REG_WB_RANGE 0x0F -#define OV7610_REG_EXP 0x10 -#define OV7610_REG_CLOCK 0x11 -#define OV7610_REG_COM_A 0x12 -#define OV7610_REG_COM_B 0x13 -#define OV7610_REG_COM_C 0x14 -#define OV7610_REG_COM_D 0x15 -#define OV7610_REG_FIELD_DIVIDE 0x16 -#define OV7610_REG_HWIN_START 0x17 -#define OV7610_REG_HWIN_END 0x18 -#define OV7610_REG_VWIN_START 0x19 -#define OV7610_REG_VWIN_END 0x1A -#define OV7610_REG_PIXEL_SHIFT 0x1B -#define OV7610_REG_ID_HIGH 0x1C -#define OV7610_REG_ID_LOW 0x1D -#define OV7610_REG_COM_E 0x20 -#define OV7610_REG_YOFFSET 0x21 -#define OV7610_REG_UOFFSET 0x22 -#define OV7610_REG_ECW 0x24 -#define OV7610_REG_ECB 0x25 -#define OV7610_REG_COM_F 0x26 -#define OV7610_REG_COM_G 0x27 -#define OV7610_REG_COM_H 0x28 -#define OV7610_REG_COM_I 0x29 -#define OV7610_REG_FRAMERATE_H 0x2A -#define OV7610_REG_FRAMERATE_L 0x2B -#define OV7610_REG_ALC 0x2C -#define OV7610_REG_COM_J 0x2D -#define OV7610_REG_VOFFSET 0x2E -#define OV7610_REG_YGAMMA 0x33 -#define OV7610_REG_BIAS_ADJUST 0x34 -#define OV7610_REG_COM_L 0x35 -#define OV7610_REG_COM_K 0x38 +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ +#define OV7610_REG_SAT 0x03 /* saturation */ + /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ + /* 08-0b reserved */ +#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ +#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */ +#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define OV7610_REG_EXP 0x10 /* manual exposure setting */ +#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define OV7610_REG_COM_A 0x12 /* misc common regs */ +#define OV7610_REG_COM_B 0x13 /* misc common regs */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_COM_D 0x15 /* misc common regs */ +#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */ +#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */ +#define OV7610_REG_VWIN_START 0x19 /* vertical window start */ +#define OV7610_REG_VWIN_END 0x1A /* vertical window end */ +#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */ + /* 0e-0f reserved */ +#define OV7610_REG_COM_E 0x20 /* misc common regs */ +#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */ +#define OV7610_REG_UOFFSET 0x22 /* U channel offset */ + /* 23 reserved */ +#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */ +#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */ +#define OV7610_REG_COM_F 0x26 /* misc settings */ +#define OV7610_REG_COM_G 0x27 /* misc settings */ +#define OV7610_REG_COM_H 0x28 /* misc settings */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ +#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */ +#define OV7610_REG_COM_J 0x2D /* misc settings */ +#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ + /* 30-32 reserved */ +#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ +#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */ +#define OV7610_REG_COM_L 0x35 /* misc settings */ + /* 36-37 reserved */ +#define OV7610_REG_COM_K 0x38 /* misc registers */ #define STREAM_BUF_SIZE (PAGE_SIZE * 4) @@ -171,8 +179,7 @@ if (debug >= level) printk("ov511: [" __PRETTY_FUNCTION__ ":%d] " fmt "\n", __LI #define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ #define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ -// FIXME - should this be 0x81 (endpoint address) or 0x01 (endpoint number)? -#define OV511_ENDPOINT_ADDRESS 0x81 /* Address of isoc endpoint */ +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ // CAMERA SPECIFIC // FIXME - these can vary between specific models @@ -223,9 +230,9 @@ enum { struct ov511_regvals { enum { - OV511_DONE_BUS, - OV511_REG_BUS, - OV511_I2C_BUS, + OV511_DONE_BUS, + OV511_REG_BUS, + OV511_I2C_BUS, } bus; unsigned char reg; unsigned char val; @@ -269,15 +276,16 @@ struct usb_ov511 { /* Device structure */ struct usb_device *dev; -#if 0 - unsigned char customid; /* Type of camera */ -#else int customid; int desc; -#endif - unsigned char iface; + int brightness; + int colour; + int contrast; + int hue; + int whiteness; + struct semaphore lock; int user; /* user count for exclusive use */ @@ -318,11 +326,34 @@ struct usb_ov511 { struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ }; - struct cam_list { int id; char *description; }; +struct palette_list { + int num; + char *name; +}; + +struct mode_list { + int width; + int height; + int mode; + u8 pxcnt; + u8 lncnt; + u8 pxdv; + u8 lndv; + u8 s_pxcnt; + u8 s_lncnt; + u8 s_pxdv; + u8 s_lndv; + u8 clock; + u8 m420; + u8 common_A; + u8 common_C; + u8 common_L; +}; + #endif diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 7f4459d2f..b40a947ed 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -16,7 +16,7 @@ #include <linux/usb.h> -static const char *version = __FILE__ ": v0.3.9 2000/04/11 Written by Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.3.12 2000/05/22 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; #define PEGASUS_MTU 1500 @@ -24,12 +24,15 @@ static const char *version = __FILE__ ": v0.3.9 2000/04/11 Written by Petko Mano #define SROM_WRITE 0x01 #define SROM_READ 0x02 #define PEGASUS_TX_TIMEOUT (HZ*5) +#define PEGASUS_RESET 1 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; + int flags; spinlock_t pegasus_lock; struct urb rx_urb, tx_urb, intr_urb; unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); @@ -44,9 +47,11 @@ struct usb_eth_dev { void *private; }; + static int loopback = 0; static int multicast_filter_limit = 32; + MODULE_AUTHOR("Petko Manolov <petkan@spct.net>"); MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); MODULE_PARM(loopback, "i"); @@ -98,6 +103,7 @@ static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regd return 1; } + static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) { int i; @@ -115,6 +121,7 @@ static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regd return 1; } + static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) { int i; @@ -134,6 +141,7 @@ static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retda return 1; } + static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) { int i; @@ -143,6 +151,7 @@ static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) return 0; } + static int pegasus_reset_mac(struct usb_device *dev) { __u8 data = 0x8; @@ -165,6 +174,7 @@ static int pegasus_reset_mac(struct usb_device *dev) return 1; } + static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) { __u16 partmedia, temp; @@ -195,13 +205,14 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) data[0] = 0xc9; data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); - data[2] = (loopback & 1) ? 0x08 : 0x00; + data[2] = (loopback & 1) ? 0x09 : 0x01; pegasus_set_registers(usb, 0, 3, data); return 0; } + static void pegasus_read_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; @@ -253,15 +264,16 @@ goon: warn("(prb)failed rx_urb %d", res); } + static void pegasus_irq(urb_t *urb) { - if(urb->status) { - __u8 *d = urb->transfer_buffer; - printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x", - d[0], d[1], d[2], d[3], d[4], d[5]); - } + __u8 *d = urb->transfer_buffer; + + if ( d[0] ) + dbg("txst0=0x%2x", d[0]); } + static void pegasus_write_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; @@ -280,12 +292,15 @@ static void pegasus_tx_timeout(struct net_device *net) struct pegasus *pegasus = net->priv; warn("%s: Tx timed out. Reseting...", net->name); + usb_unlink_urb(&pegasus->tx_urb); pegasus->stats.tx_errors++; net->trans_start = jiffies; + pegasus->flags |= PEGASUS_RESET; netif_wake_queue(net); } + static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -317,11 +332,13 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) return 0; } + static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) { return &((struct pegasus *)dev->priv)->stats; } + static int pegasus_open(struct net_device *net) { struct pegasus *pegasus = (struct pegasus *)net->priv; @@ -334,8 +351,10 @@ static int pegasus_open(struct net_device *net) if ((res = usb_submit_urb(&pegasus->rx_urb))) warn("(open)failed rx_urb %d", res); - -/* usb_submit_urb(&pegasus->intr_urb);*/ + + if ((res = usb_submit_urb(&pegasus->intr_urb))) + warn("(open)failed intr_urb %d", res); + netif_start_queue(net); MOD_INC_USE_COUNT; @@ -343,6 +362,7 @@ static int pegasus_open(struct net_device *net) return 0; } + static int pegasus_close(struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -351,13 +371,14 @@ static int pegasus_close(struct net_device *net) usb_unlink_urb(&pegasus->rx_urb); usb_unlink_urb(&pegasus->tx_urb); -/* usb_unlink_urb(&pegasus->intr_urb); */ + usb_unlink_urb(&pegasus->intr_urb); MOD_DEC_USE_COUNT; return 0; } + static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) { __u16 *data = (__u16 *)&rq->ifr_data; @@ -379,6 +400,7 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) } } + static void pegasus_set_rx_mode(struct net_device *net) { struct pegasus *pegasus = net->priv; @@ -400,6 +422,7 @@ static void pegasus_set_rx_mode(struct net_device *net) netif_wake_queue(net); } + static int check_device_ids( __u16 vendor, __u16 product ) { int i=0; @@ -413,6 +436,7 @@ static int check_device_ids( __u16 vendor, __u16 product ) return -1; } + static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) { struct net_device *net; @@ -463,7 +487,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk, pegasus); FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), - pegasus->intr_buff, 8, pegasus_irq, pegasus, 250); + pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); @@ -471,6 +495,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) return pegasus; } + static void pegasus_disconnect(struct usb_device *dev, void *ptr) { struct pegasus *pegasus = ptr; @@ -487,11 +512,12 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) usb_unlink_urb(&pegasus->rx_urb); usb_unlink_urb(&pegasus->tx_urb); -/* usb_unlink_urb(&pegasus->intr_urb);*/ + usb_unlink_urb(&pegasus->intr_urb); kfree(pegasus); } + static struct usb_driver pegasus_driver = { name: "pegasus", probe: pegasus_probe, diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index d64258e8e..c013a8154 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -1,5 +1,5 @@ /* - * printer.c Version 0.3 + * printer.c Version 0.4 * * Copyright (c) 1999 Michael Gee <michael@linuxspecific.com> * Copyright (c) 1999 Pavel Machek <pavel@suse.cz> @@ -13,6 +13,7 @@ * v0.1 - thorough cleaning, URBification, almost a rewrite * v0.2 - some more cleanups * v0.3 - cleaner again, waitqueue fixes + * v0.4 - fixes in unidirectional mode */ /* @@ -102,7 +103,7 @@ static void usblp_bulk(struct urb *urb) return; if (urb->status) - warn("nonzero read bulk status received: %d", urb->status); + warn("nonzero read/write bulk status received: %d", urb->status); wake_up_interruptible(&usblp->wait); } @@ -172,9 +173,12 @@ static int usblp_open(struct inode *inode, struct file *file) usblp->writeurb.transfer_buffer_length = 0; usblp->writeurb.status = 0; - usblp->readcount = 0; - usb_submit_urb(&usblp->readurb); + if (usblp->bidir) { + usblp->readcount = 0; + usb_submit_urb(&usblp->readurb); + } + return 0; } @@ -185,7 +189,8 @@ static int usblp_release(struct inode *inode, struct file *file) usblp->used = 0; if (usblp->dev) { - usb_unlink_urb(&usblp->readurb); + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); MOD_DEC_USE_COUNT; return 0; @@ -203,8 +208,8 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait { struct usblp *usblp = file->private_data; poll_wait(file, &usblp->wait, wait); - return (usblp->readurb.status == -EINPROGRESS ? 0 : POLLIN | POLLRDNORM) - | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); + return ((usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) + | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); } static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) @@ -315,7 +320,6 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) int minor, i, alts = -1, bidir = 0; char *buf; - for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { interface = &dev->actconfig->interface[ifnum].altsetting[i]; @@ -342,22 +346,21 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) err("can't set desired altsetting %d on interface %d", alts, ifnum); epwrite = interface->endpoint + 0; - epread = NULL; - - if (bidir) { - epread = interface->endpoint + 1; - if ((epread->bEndpointAddress & 0x80) != 0x80) { - epwrite = interface->endpoint + 1; - epread = interface->endpoint + 0; + epread = bidir ? interface->endpoint + 1 : NULL; - if ((epread->bEndpointAddress & 0x80) != 0x80) - return NULL; - } + if ((epwrite->bEndpointAddress & 0x80) == 0x80) { + if (interface->bNumEndpoints == 1) + return NULL; + epwrite = interface->endpoint + 1; + epread = bidir ? interface->endpoint + 0 : NULL; } if ((epwrite->bEndpointAddress & 0x80) == 0x80) return NULL; + if (bidir && (epread->bEndpointAddress & 0x80) != 0x80) + return NULL; + for (minor = 0; minor < USBLP_MINORS && usblp_table[minor]; minor++); if (usblp_table[minor]) { err("no more free usblp devices"); @@ -386,10 +389,9 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), buf, 0, usblp_bulk, usblp); - if (bidir) { + if (bidir) FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); - } info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); @@ -408,8 +410,9 @@ static void usblp_disconnect(struct usb_device *dev, void *ptr) usblp->dev = NULL; - usb_unlink_urb(&usblp->readurb); usb_unlink_urb(&usblp->writeurb); + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); kfree(usblp->writeurb.transfer_buffer); diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile index fa69fa52a..7d24c34f9 100644 --- a/drivers/usb/serial/Makefile +++ b/drivers/usb/serial/Makefile @@ -4,8 +4,26 @@ O_TARGET := usb-serial.o M_OBJS := usb-serial.o -O_OBJS := usbserial.o visor.o whiteheat.o ftdi_sio.o keyspan_pda.o omninet.o digi_acceleport.o +O_OBJS := usbserial.o MOD_LIST_NAME := USB_SERIAL_MODULES +ifeq ($(CONFIG_USB_SERIAL_VISOR),y) + O_OBJS += visor.o +endif +ifeq ($(CONFIG_USB_SERIAL_WHITEHEAT),y) + O_OBJS += whiteheat.o +endif +ifeq ($(CONFIG_USB_SERIAL_FTDI_SIO),y) + O_OBJS += ftdi_sio.o +endif +ifeq ($(CONFIG_USB_SERIAL_KEYSPAN_PDA),y) + O_OBJS += keyspan_pda.o +endif +ifeq ($(CONFIG_USB_SERIAL_OMNINET),y) + O_OBJS += omninet.o +endif +ifeq ($(CONFIG_USB_SERIAL_DIGI_ACCELEPORT),y) + O_OBJS += digi_acceleport.o +endif + include $(TOPDIR)/Rules.make - diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 6700f9e01..fe2d2013b 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -14,16 +14,39 @@ * Peter Berger (pberger@brimson.com) * Al Borchers (borchers@steinerpoint.com) * +* (5/16/2000) pberger and borchers +* -- added timeouts to sleeps +* -- handle transition to/from B0 in digi_set_termios +* +* (5/13/2000) pberger and borchers +* -- all commands now sent on out of band port, using digi_write_oob +* -- get modem control signals whenever they change, support TIOCMGET/ +* SET/BIS/BIC ioctls +* -- digi_set_termios now supports parity, word size, stop bits, and +* receive enable +* -- cleaned up open and close, use digi_set_termios and digi_write_oob +* to set port parameters +* -- added digi_startup_device to start read chains on all ports +* -- write buffer is only used when count==1, to be sure put_char can +* write a char (unless the buffer is full) +* +* (5/10/2000) pberger and borchers +* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls +* -- Fixed problem where the first incoming character is lost on +* port opens after the first close on that port. Now we keep +* the read_urb chain open until shutdown. +* -- Added more port conditioning calls in digi_open and digi_close. +* -- Convert port->active to a use count so that we can deal with multiple +* opens and closes properly. +* -- Fixed some problems with the locking code. +* * (5/3/2000) pberger and borchers -* First alpha version of the driver--many known limitations and bugs. +* -- First alpha version of the driver--many known limitations and bugs. * -* $Id: digi_acceleport.c,v 1.28 2000/05/04 01:47:08 root Exp root $ +* $Id: digi_acceleport.c,v 1.43 2000/05/17 03:21:38 root Exp root $ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -37,45 +60,57 @@ #include <linux/tty.h> #include <linux/module.h> #include <linux/spinlock.h> +#include <linux/usb.h> +#include "usb-serial.h" #ifdef CONFIG_USB_SERIAL_DEBUG #define DEBUG #else #undef DEBUG #endif -#include <linux/usb.h> -#include "usb-serial.h" /* Defines */ /* port buffer length -- must be <= transfer buffer length - 2 */ /* so we can be sure to send the full buffer in one urb */ -#define DIGI_PORT_BUF_LEN 16 +#define DIGI_PORT_BUF_LEN 16 + +/* retry timeout while waiting for urb->status to go to 0 */ +#define DIGI_RETRY_TIMEOUT (HZ/10) /* AccelePort USB Defines */ /* ids */ -#define DIGI_VENDOR_ID 0x05c5 -#define DIGI_ID 0x0004 - -/* commands */ -#define DIGI_CMD_SET_BAUD_RATE 0 -#define DIGI_CMD_SET_WORD_SIZE 1 -#define DIGI_CMD_SET_PARITY 2 -#define DIGI_CMD_SET_STOP_BITS 3 -#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 -#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 -#define DIGI_CMD_SET_DTR_SIGNAL 6 -#define DIGI_CMD_SET_RTS_SIGNAL 7 -#define DIGI_CMD_RECEIVE_ENABLE 10 -#define DIGI_CMD_BREAK_CONTROL 11 -#define DIGI_CMD_LOCAL_LOOPBACK 12 -#define DIGI_CMD_TRANSMIT_IDLE 13 -#define DIGI_CMD_WRITE_UART_REGISTER 15 -#define DIGI_CMD_AND_UART_REGISTER 16 -#define DIGI_CMD_OR_UART_REGISTER 17 -#define DIGI_CMD_SEND_DATA 18 +#define DIGI_VENDOR_ID 0x05c5 +#define DIGI_ID 0x0004 + +/* commands + * "INB": can be used on the in-band endpoint + * "OOB": can be used on the out-of-band endpoint + */ +#define DIGI_CMD_SET_BAUD_RATE 0 /* INB, OOB */ +#define DIGI_CMD_SET_WORD_SIZE 1 /* INB, OOB */ +#define DIGI_CMD_SET_PARITY 2 /* INB, OOB */ +#define DIGI_CMD_SET_STOP_BITS 3 /* INB, OOB */ +#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 /* INB, OOB */ +#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 /* INB, OOB */ +#define DIGI_CMD_SET_DTR_SIGNAL 6 /* INB, OOB */ +#define DIGI_CMD_SET_RTS_SIGNAL 7 /* INB, OOB */ +#define DIGI_CMD_READ_INPUT_SIGNALS 8 /* OOB */ +#define DIGI_CMD_IFLUSH_FIFO 9 /* OOB */ +#define DIGI_CMD_RECEIVE_ENABLE 10 /* INB, OOB */ +#define DIGI_CMD_BREAK_CONTROL 11 /* INB, OOB */ +#define DIGI_CMD_LOCAL_LOOPBACK 12 /* INB, OOB */ +#define DIGI_CMD_TRANSMIT_IDLE 13 /* INB, OOB */ +#define DIGI_CMD_READ_UART_REGISTER 14 /* OOB */ +#define DIGI_CMD_WRITE_UART_REGISTER 15 /* INB, OOB */ +#define DIGI_CMD_AND_UART_REGISTER 16 /* INB, OOB */ +#define DIGI_CMD_OR_UART_REGISTER 17 /* INB, OOB */ +#define DIGI_CMD_SEND_DATA 18 /* INB */ +#define DIGI_CMD_RECEIVE_DATA 19 /* INB */ +#define DIGI_CMD_RECEIVE_DISABLE 20 /* INB */ +#define DIGI_CMD_GET_PORT_TYPE 21 /* OOB */ /* baud rates */ #define DIGI_BAUD_50 0 @@ -102,10 +137,71 @@ #define DIGI_BAUD_230400 21 #define DIGI_BAUD_460800 22 -/* flow control arguments */ -#define DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL 1 -#define DIGI_ENABLE_RTS_CTS_FLOW_CONTROL 2 -#define DIGI_ENABLE_DTR_DSR_FLOW_CONTROL 4 +/* arguments */ +#define DIGI_WORD_SIZE_5 0 +#define DIGI_WORD_SIZE_6 1 +#define DIGI_WORD_SIZE_7 2 +#define DIGI_WORD_SIZE_8 3 + +#define DIGI_PARITY_NONE 0 +#define DIGI_PARITY_ODD 1 +#define DIGI_PARITY_EVEN 2 +#define DIGI_PARITY_MARK 3 +#define DIGI_PARITY_SPACE 4 + +#define DIGI_STOP_BITS_1 0 +#define DIGI_STOP_BITS_2 1 + +#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_INPUT_FLOW_CONTROL_RTS 2 +#define DIGI_INPUT_FLOW_CONTROL_DTR 4 + +#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_OUTPUT_FLOW_CONTROL_CTS 2 +#define DIGI_OUTPUT_FLOW_CONTROL_DSR 4 + +#define DIGI_DTR_INACTIVE 0 +#define DIGI_DTR_ACTIVE 1 +#define DIGI_DTR_INPUT_FLOW_CONTROL 2 + +#define DIGI_RTS_INACTIVE 0 +#define DIGI_RTS_ACTIVE 1 +#define DIGI_RTS_INPUT_FLOW_CONTROL 2 +#define DIGI_RTS_TOGGLE 3 + +#define DIGI_FLUSH_TX 1 +#define DIGI_FLUSH_RX 2 +#define DIGI_RESUME_TX 4 /* clears xoff condition */ + +#define DIGI_DISABLE 0 +#define DIGI_ENABLE 1 + +#define DIGI_DEASSERT 0 +#define DIGI_ASSERT 1 + +/* in band status codes */ +#define DIGI_OVERRUN_ERROR 4 +#define DIGI_PARITY_ERROR 8 +#define DIGI_FRAMING_ERROR 16 +#define DIGI_BREAK_ERROR 32 + +/* out of band status */ +#define DIGI_NO_ERROR 0 +#define DIGI_BAD_FIRST_PARAMETER 1 +#define DIGI_BAD_SECOND_PARAMETER 2 +#define DIGI_INVALID_LINE 3 +#define DIGI_INVALID_OPCODE 4 + +/* input signals */ +#define DIGI_READ_INPUT_SIGNALS_SLOT 1 +#define DIGI_READ_INPUT_SIGNALS_ERR 2 +#define DIGI_READ_INPUT_SIGNALS_BUSY 4 +#define DIGI_READ_INPUT_SIGNALS_PE 8 +#define DIGI_READ_INPUT_SIGNALS_CTS 16 +#define DIGI_READ_INPUT_SIGNALS_DSR 32 +#define DIGI_READ_INPUT_SIGNALS_RI 64 +#define DIGI_READ_INPUT_SIGNALS_DCD 128 + /* macros */ #define MAX(a,b) (((a)>(b))?(a):(b)) @@ -117,32 +213,21 @@ typedef struct digi_private { spinlock_t dp_port_lock; int dp_buf_len; - char dp_buf[32]; + unsigned char dp_buf[DIGI_PORT_BUF_LEN]; + unsigned int dp_modem_signals; } digi_private_t; -struct s_digiusb { - u8 opcode; - u8 length; - u8 val; - u8 pad; -}; - /* Local Function Declarations */ -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ); -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ); +static int digi_write_oob( unsigned char *buf, int count ); +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ); static void digi_rx_throttle (struct usb_serial_port *port); static void digi_rx_unthrottle (struct usb_serial_port *port); -static int digi_setbaud( struct usb_serial_port *port, int baud ); static void digi_set_termios( struct usb_serial_port *port, struct termios *old_termios ); static void digi_break_ctl( struct usb_serial_port *port, int break_state ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ); -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ); static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ); static int digi_write( struct usb_serial_port *port, int from_user, @@ -152,138 +237,186 @@ static int digi_write_room( struct usb_serial_port *port ); static int digi_chars_in_buffer( struct usb_serial_port *port ); static int digi_open( struct usb_serial_port *port, struct file *filp ); static void digi_close( struct usb_serial_port *port, struct file *filp ); -static int digi_startup (struct usb_serial *serial); +static int digi_startup_device( struct usb_serial *serial ); +static int digi_startup( struct usb_serial *serial ); static void digi_shutdown( struct usb_serial *serial ); static void digi_read_bulk_callback( struct urb *urb ); +static void digi_read_oob( struct urb *urb ); /* Statics */ /* device info needed for the Digi serial converter */ -static __u16 digi_vendor_id = DIGI_VENDOR_ID; -static __u16 digi_product_id = DIGI_ID; +static __u16 digi_vendor_id = DIGI_VENDOR_ID; +static __u16 digi_product_id = DIGI_ID; /* out of band port */ -static int oob_port_num; /* index of out-of-band port */ -static struct usb_serial_port *oob_port; /* out-of-band control port */ -static int oob_read_started = 0; +static int oob_port_num; /* index of out-of-band port */ +static struct usb_serial_port *oob_port; /* out-of-band port */ +static int device_startup = 0; -/* config lock -- used to protect digi statics and globals, like oob vars */ -spinlock_t config_lock; +/* startup lock -- used to by digi_startup_device */ +spinlock_t startup_lock; /* Globals */ struct usb_serial_device_type digi_acceleport_device = { - name: "Digi USB", - idVendor: &digi_vendor_id, - idProduct: &digi_product_id, - needs_interrupt_in: DONT_CARE, - needs_bulk_in: MUST_HAVE, - needs_bulk_out: MUST_HAVE, - num_interrupt_in: 0, - num_bulk_in: 5, - num_bulk_out: 5, - num_ports: 4, - open: digi_open, - close: digi_close, - write: digi_write, - write_room: digi_write_room, - write_bulk_callback: digi_write_bulk_callback, - read_bulk_callback: digi_read_bulk_callback, - chars_in_buffer: digi_chars_in_buffer, - throttle: digi_rx_throttle, - unthrottle: digi_rx_unthrottle, - ioctl: digi_ioctl, - set_termios: digi_set_termios, - break_ctl: digi_break_ctl, - startup: digi_startup, - shutdown: digi_shutdown, + name: "Digi USB", + idVendor: &digi_vendor_id, + idProduct: &digi_product_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 5, + num_bulk_out: 5, + num_ports: 4, + open: digi_open, + close: digi_close, + write: digi_write, + write_room: digi_write_room, + write_bulk_callback: digi_write_bulk_callback, + read_bulk_callback: digi_read_bulk_callback, + chars_in_buffer: digi_chars_in_buffer, + throttle: digi_rx_throttle, + unthrottle: digi_rx_unthrottle, + ioctl: digi_ioctl, + set_termios: digi_set_termios, + break_ctl: digi_break_ctl, + startup: digi_startup, + shutdown: digi_shutdown, }; /* Functions */ -/* Send message on the out-of-Band endpoint */ -static void digi_send_oob( char *mes, int opcode, int linenum, int data1, int data2 ) +/* +* Digi Write OOB +* +* Write commands on the out of band port. Commands are 4 +* bytes each, multiple commands can be sent at once, and +* no command will be split across USB packets. Returns 0 +* if successful, -EINTR if interrupted while sleeping, or +* a negative error returned by usb_submit_urb. +*/ + +static int digi_write_oob( unsigned char *buf, int count ) { - int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(oob_port->private); + int ret = 0; + int len; + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); -dbg( "digi_send_oob: TOP: from '%s', opcode: %d, linenum:%d, data1: %d, data2: %d", mes, opcode, linenum, data1, data2 ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)linenum; - digiusb.val = (u8)data1; - digiusb.pad = (u8)data2; +dbg( "digi_write_oob: TOP: port=%d, count=%d", oob_port->number, count ); - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); - while (oob_port->write_urb->status == -EINPROGRESS) { -dbg( "digi_send_oob: opcode:%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on(&oob_port->write_wait); - if (signal_pending(current)) { - return; + while( count > 0 ) { + + while( oob_port->write_urb->status == -EINPROGRESS ) { + spin_unlock( &oob_priv->dp_port_lock ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock( &oob_priv->dp_port_lock ); + } + + /* len must be a multiple of 4, so commands are not split */ + len = MIN( count, oob_port->bulk_out_size ); + if( len > 4 ) + len &= ~3; + + memcpy( oob_port->write_urb->transfer_buffer, buf, len ); + oob_port->write_urb->transfer_buffer_length = len; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + count -= len; + buf += len; + } else { + dbg( "digi_write_oob: usb_submit_urb failed, ret=%d", + ret ); + break; } - spin_lock( &priv->dp_port_lock ); - } - memcpy( oob_port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - oob_port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(oob_port->write_urb)) != 0 ) { - dbg( - "digi_send_oob: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); } - spin_unlock( &priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); -dbg( "digi_send_oob: opcode %d done", opcode ); + return( ret ); } -static void digi_send_cmd( char *mes, struct usb_serial_port *port, int opcode, - int length, int val ) +/* +* Digi Set Modem Signals +* +* Sets or clears DTR and RTS on the port, according to the +* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags +* for the modem_signals argument. Returns 0 if successful, +* -EINTR if interrupted while sleeping, or a non-zero error +* returned by usb_submit_urb. +*/ + +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ) { int ret; - struct s_digiusb digiusb; - digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = oob_port->write_urb->transfer_buffer; + digi_private_t *port_priv = (digi_private_t *)(port->private); + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); -dbg( "digi_send_cmd: TOP: from '%s', opcode: %d, val: %d", mes, opcode, val ); +dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", +port->number, modem_signals ); - digiusb.opcode = (u8)opcode; - digiusb.length = (u8)length; - digiusb.val = (u8)val; - digiusb.pad = 0; + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); - spin_lock( &priv->dp_port_lock ); - - while( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_send_cmd: opcode=%d already writing...", opcode ); - spin_unlock( &priv->dp_port_lock ); - interruptible_sleep_on( &port->write_wait ); + while( oob_port->write_urb->status == -EINPROGRESS ) { + spin_unlock( &port_priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); if( signal_pending(current) ) { - return; + return( -EINTR ); } - spin_lock( &priv->dp_port_lock ); + spin_lock( &oob_priv->dp_port_lock ); + spin_lock( &port_priv->dp_port_lock ); } - memcpy( port->write_urb->transfer_buffer, &digiusb, sizeof(digiusb) ); - port->write_urb->transfer_buffer_length = sizeof(digiusb); - if( (ret=usb_submit_urb(port->write_urb)) != 0 ) - dbg( - "digi_send_cmd: usb_submit_urb(write bulk) failed, opcode=%d, ret=%d", - opcode, ret ); + /* command is 4 bytes: command, line, argument, pad */ + data[0] = DIGI_CMD_SET_DTR_SIGNAL; + data[1] = port->number; + data[2] = (modem_signals&TIOCM_DTR) ? + DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; + data[3] = 0; + + data[4] = DIGI_CMD_SET_RTS_SIGNAL; + data[5] = port->number; + data[6] = (modem_signals&TIOCM_RTS) ? + DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; + data[7] = 0; + + oob_port->write_urb->transfer_buffer_length = 8; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + port_priv->dp_modem_signals = + (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) + | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); + } else { + dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + ret ); + } -dbg( "digi_send_cmd: opcode %d done", opcode ); + spin_unlock( &port_priv->dp_port_lock ); + spin_unlock( &oob_priv->dp_port_lock ); - spin_unlock( &priv->dp_port_lock ); + return( ret ); } @@ -312,52 +445,7 @@ dbg( "digi_rx_unthrottle: TOP: port=%d", port->number ); /* just restart the receive interrupt URB */ //if (usb_submit_urb(port->interrupt_in_urb)) - // dbg( "digi_rx_unthrottle: usb_submit_urb(read urb) failed" ); - -} - - -static int digi_setbaud( struct usb_serial_port *port, int baud ) -{ - - int bindex; - - -dbg( "digi_setbaud: TOP: port=%d", port->number ); - - switch( baud ) { - case 50: bindex = DIGI_BAUD_50; break; - case 75: bindex = DIGI_BAUD_75; break; - case 110: bindex = DIGI_BAUD_110; break; - case 150: bindex = DIGI_BAUD_150; break; - case 200: bindex = DIGI_BAUD_200; break; - case 300: bindex = DIGI_BAUD_300; break; - case 600: bindex = DIGI_BAUD_600; break; - case 1200: bindex = DIGI_BAUD_1200; break; - case 1800: bindex = DIGI_BAUD_1800; break; - case 2400: bindex = DIGI_BAUD_2400; break; - case 4800: bindex = DIGI_BAUD_4800; break; - case 7200: bindex = DIGI_BAUD_7200; break; - case 9600: bindex = DIGI_BAUD_9600; break; - case 14400: bindex = DIGI_BAUD_14400; break; - case 19200: bindex = DIGI_BAUD_19200; break; - case 28800: bindex = DIGI_BAUD_28800; break; - case 38400: bindex = DIGI_BAUD_38400; break; - case 57600: bindex = DIGI_BAUD_57600; break; - case 76800: bindex = DIGI_BAUD_76800; break; - case 115200: bindex = DIGI_BAUD_115200; break; - case 153600: bindex = DIGI_BAUD_153600; break; - case 230400: bindex = DIGI_BAUD_230400; break; - case 460800: bindex = DIGI_BAUD_460800; break; - default: - dbg( "digi_setbaud: can't handle requested baud rate %d", baud ); - return( -EINVAL ); - break; - } - - digi_send_cmd( "digi_setbaud:", port, DIGI_CMD_SET_BAUD_RATE, 2, bindex ); - - return( 0 ); /* FIX -- send_cmd should return a value??, return it */ + // dbg( "digi_rx_unthrottle: usb_submit_urb failed" ); } @@ -367,167 +455,243 @@ static void digi_set_termios( struct usb_serial_port *port, { unsigned int iflag = port->tty->termios->c_iflag; - unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; + unsigned int old_iflag = old_termios->c_iflag; unsigned int old_cflag = old_termios->c_cflag; - int arg; + unsigned char buf[32]; + int arg,ret; + int i = 0; dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", port->number, iflag, old_iflag, cflag, old_cflag ); /* set baud rate */ - /* if( (cflag&CBAUD) != (old_cflag&CBAUD) ) */ { + if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + + arg = -1; + + /* reassert DTR and (maybe) RTS on transition from B0 */ + if( (old_cflag&CBAUD) == B0 ) { + /* don't set RTS if using hardware flow control */ + /* and throttling input -- not implemented yet */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + } + switch( (cflag&CBAUD) ) { - case B50: digi_setbaud(port, 50); break; - case B75: digi_setbaud(port, 75); break; - case B110: digi_setbaud(port, 110); break; - case B150: digi_setbaud(port, 150); break; - case B200: digi_setbaud(port, 200); break; - case B300: digi_setbaud(port, 300); break; - case B600: digi_setbaud(port, 600); break; - case B1200: digi_setbaud(port, 1200); break; - case B1800: digi_setbaud(port, 1800); break; - case B2400: digi_setbaud(port, 2400); break; - case B4800: digi_setbaud(port, 4800); break; - case B9600: digi_setbaud(port, 9600); break; - case B19200: digi_setbaud(port, 19200); break; - case B38400: digi_setbaud(port, 38400); break; - case B57600: digi_setbaud(port, 57600); break; - case B115200: digi_setbaud(port, 115200); break; - default: - dbg( "digi_set_termios: can't handle baud rate 0x%x", - (cflag&CBAUD) ); - break; + /* drop DTR and RTS on transition to B0 */ + case B0: digi_set_modem_signals( port, 0 ); break; + case B50: arg = DIGI_BAUD_50; break; + case B75: arg = DIGI_BAUD_75; break; + case B110: arg = DIGI_BAUD_110; break; + case B150: arg = DIGI_BAUD_150; break; + case B200: arg = DIGI_BAUD_200; break; + case B300: arg = DIGI_BAUD_300; break; + case B600: arg = DIGI_BAUD_600; break; + case B1200: arg = DIGI_BAUD_1200; break; + case B1800: arg = DIGI_BAUD_1800; break; + case B2400: arg = DIGI_BAUD_2400; break; + case B4800: arg = DIGI_BAUD_4800; break; + case B9600: arg = DIGI_BAUD_9600; break; + case B19200: arg = DIGI_BAUD_19200; break; + case B38400: arg = DIGI_BAUD_38400; break; + case B57600: arg = DIGI_BAUD_57600; break; + case B115200: arg = DIGI_BAUD_115200; break; + case B230400: arg = DIGI_BAUD_230400; break; + case B460800: arg = DIGI_BAUD_460800; break; + default: + dbg( "digi_set_termios: can't handle baud rate 0x%x", + (cflag&CBAUD) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_BAUD_RATE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set parity */ + if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + + if( (cflag&PARENB) ) { + if( (cflag&PARODD) ) + arg = DIGI_PARITY_ODD; + else + arg = DIGI_PARITY_EVEN; + } else { + arg = DIGI_PARITY_NONE; } + + buf[i++] = DIGI_CMD_SET_PARITY; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set word size */ + if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + + arg = -1; + + switch( (cflag&CSIZE) ) { + case CS5: arg = DIGI_WORD_SIZE_5; break; + case CS6: arg = DIGI_WORD_SIZE_6; break; + case CS7: arg = DIGI_WORD_SIZE_7; break; + case CS8: arg = DIGI_WORD_SIZE_8; break; + default: + dbg( "digi_set_termios: can't handle word size %d", + (cflag&CSIZE) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_WORD_SIZE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set stop bits */ + if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { + + if( (cflag&CSTOPB) ) + arg = DIGI_STOP_BITS_2; + else + arg = DIGI_STOP_BITS_1; + + buf[i++] = DIGI_CMD_SET_STOP_BITS; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } /* set input flow control */ - /* if( (iflag&IXOFF) != (old_iflag&IXOFF) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { arg = 0; if( (iflag&IXOFF) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_INPUT_FLOW_CONTROL_RTS; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; - digi_send_cmd( "digi_termios: set input flow control:", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } /* set output flow control */ - /* if( (iflag&IXON) != (old_iflag&IXON) - || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) */ { + /*if( (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) )*/ { arg = 0; if( (iflag&IXON) ) - arg |= DIGI_ENABLE_IXON_IXOFF_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + if( (cflag&CRTSCTS) ) - arg |= DIGI_ENABLE_RTS_CTS_FLOW_CONTROL; + arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; - digi_send_cmd( "digi_set_termios: set output flow control:", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, arg ); + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; } -} + /* set receive enable/disable */ + if( (cflag&CREAD) != (old_cflag&CREAD) ) { + if( (cflag&CREAD) ) + arg = DIGI_ENABLE; + else + arg = DIGI_DISABLE; -static void digi_break_ctl( struct usb_serial_port *port, int break_state ) -{ -dbg( "digi_break_ctl: TOP: port=%d", port->number ); -} + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = arg; + buf[i++] = 0; + } -/* modem control pins: DTR and RTS are outputs and can be controlled; - DCD, RI, DSR, CTS are inputs and can be read */ + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_set_termios: write oob failed, ret=%d", ret ); -static int digi_get_modem_info( struct usb_serial *serial, - unsigned char *value ) -{ -dbg( "digi_get_modem_info: TOP" ); - return( 0 ); } -static int digi_set_modem_info( struct usb_serial *serial, - unsigned char value ) +static void digi_break_ctl( struct usb_serial_port *port, int break_state ) { -dbg( "digi_set_modem_info: TOP" ); - return( 0 ); +dbg( "digi_break_ctl: TOP: port=%d", port->number ); } static int digi_ioctl( struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg ) { - struct usb_serial *serial = port->serial; - int rc; - unsigned int value; - unsigned char status, mask; + + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned int val; + dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", port->number, cmd ); -return( -ENOIOCTLCMD ); switch (cmd) { - case TIOCMGET: /* get modem pins state */ - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - value = - ((status & (1<<7)) ? TIOCM_DTR : 0) | - ((status & (1<<6)) ? TIOCM_CAR : 0) | - ((status & (1<<5)) ? TIOCM_RNG : 0) | - ((status & (1<<4)) ? TIOCM_DSR : 0) | - ((status & (1<<3)) ? TIOCM_CTS : 0) | - ((status & (1<<2)) ? TIOCM_RTS : 0); - if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) - return -EFAULT; - return 0; - case TIOCMSET: /* set a state as returned by MGET */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - status = - ((value & TIOCM_DTR) ? (1<<7) : 0) | - ((value & TIOCM_CAR) ? (1<<6) : 0) | - ((value & TIOCM_RNG) ? (1<<5) : 0) | - ((value & TIOCM_DSR) ? (1<<4) : 0) | - ((value & TIOCM_CTS) ? (1<<3) : 0) | - ((value & TIOCM_RTS) ? (1<<2) : 0); - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; - case TIOCMBIS: /* set bits in bitmask <arg> */ - case TIOCMBIC: /* clear bits from bitmask <arg> */ - if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) - return -EFAULT; - rc = digi_get_modem_info(serial, &status); - if (rc < 0) - return rc; - mask = - ((value & TIOCM_RTS) ? (1<<2) : 0) | - ((value & TIOCM_DTR) ? (1<<7) : 0); - if (cmd == TIOCMBIS) - status |= mask; - else - status &= ~mask; - rc = digi_set_modem_info(serial, status); - if (rc < 0) - return rc; - return 0; + + case TIOCMGET: + spin_lock( &priv->dp_port_lock ); + val = priv->dp_modem_signals; + spin_unlock( &priv->dp_port_lock ); + if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) ) + return( -EFAULT ); + return( 0 ); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) ) + return( -EFAULT ); + spin_lock( &priv->dp_port_lock ); + if( cmd == TIOCMBIS ) + val = priv->dp_modem_signals | val; + else if( cmd == TIOCMBIC ) + val = priv->dp_modem_signals & ~val; + spin_unlock( &priv->dp_port_lock ); + return( digi_set_modem_signals( port, val ) ); + case TIOCMIWAIT: /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ /* TODO */ + return( 0 ); + case TIOCGICOUNT: /* return count of modemline transitions */ - return 0; /* TODO */ + /* TODO */ + return 0; + } - - return -ENOIOCTLCMD; + + return( -ENOIOCTLCMD ); + } @@ -535,7 +699,7 @@ static int digi_write( struct usb_serial_port *port, int from_user, const unsigned char *buf, int count ) { - int i,ret,data_len,new_len; + int ret,data_len,new_len; digi_private_t *priv = (digi_private_t *)(port->private); @@ -550,16 +714,18 @@ port->number, count, from_user, in_interrupt() ); /* wait for urb status clear to submit another urb */ if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_write: -EINPROGRESS set" ); - - /* buffer the data if possible */ - new_len = MIN( count, DIGI_PORT_BUF_LEN-priv->dp_buf_len ); - memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); - priv->dp_buf_len += new_len; + /* buffer data if count is 1 (probably put_char) if possible */ + if( count == 1 ) { + new_len = MIN( count, + DIGI_PORT_BUF_LEN-priv->dp_buf_len ); + memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); + priv->dp_buf_len += new_len; + } else { + new_len = 0; + } - /* unlock and return number of bytes buffered */ spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write: buffering, return %d", new_len ); + return( new_len ); } @@ -569,19 +735,16 @@ dbg( "digi_write: buffering, return %d", new_len ); new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); data_len = new_len + priv->dp_buf_len; -dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new_len, priv->dp_buf_len, data_len, port->bulk_out_size-2 ); - - /* nothing to send */ if( data_len == 0 ) { spin_unlock( &priv->dp_port_lock ); return( 0 ); } - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) = (u8)data_len; + *((unsigned char *)(port->write_urb->transfer_buffer)) + = (unsigned char)DIGI_CMD_SEND_DATA; + *((unsigned char *)(port->write_urb->transfer_buffer)+1) + = (unsigned char)data_len; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = data_len+2; /* copy in buffered data first */ @@ -590,33 +753,34 @@ dbg( "digi_write: counts: new data %d, buf data %d, total data %d (max %d)", new /* copy in new data */ if( from_user ) { - copy_from_user( port->write_urb->transfer_buffer+2+priv->dp_buf_len, + copy_from_user( + port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); - } - else { + } else { memcpy( port->write_urb->transfer_buffer+2+priv->dp_buf_len, buf, new_len ); } -#ifdef DEBUG - printk( KERN_DEBUG __FILE__ ": digi_write: length=%d, data=", - port->write_urb->transfer_buffer_length ); +#ifdef DEBUG_DATA +{ + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + port->number, port->write_urb->transfer_buffer_length ); for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { printk( "%.2x ", - ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); } printk( "\n" ); +} #endif - /* submit urb */ if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - /* submit successful, return length of new data written */ ret = new_len; - /* clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write: usb_submit_urb failed, ret=%d", + ret ); /* no bytes written - should we return the error code or 0? */ ret = 0; } @@ -658,28 +822,34 @@ dbg( "digi_write_bulk_callback: TOP: port=%d", port->number ); spin_lock( &priv->dp_port_lock ); if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) { - /* set command and length bytes */ - *((u8 *)(port->write_urb->transfer_buffer)) - = (u8)DIGI_CMD_SEND_DATA; - *((u8 *)(port->write_urb->transfer_buffer)+1) - = (u8)priv->dp_buf_len; + *((unsigned char *)(port->write_urb->transfer_buffer)) + = (unsigned char)DIGI_CMD_SEND_DATA; + *((unsigned char *)(port->write_urb->transfer_buffer)+1) + = (unsigned char)priv->dp_buf_len; - /* set total transfer buffer length */ port->write_urb->transfer_buffer_length = priv->dp_buf_len+2; - /* copy in buffered data */ memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, priv->dp_buf_len ); - /* submit urb */ -dbg( "digi_write_bulk_callback: submit urb to write buffer, data len=%d", -priv->dp_buf_len ); +#ifdef DEBUG_DATA +{ + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=", + port->number, port->write_urb->transfer_buffer_length ); + for( i=0; i<port->write_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); +} +#endif + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { - /* successful, clear buffer */ priv->dp_buf_len = 0; - } - else { - dbg( "digi_write_bulk_callback: usb_submit_urb(write bulk) failed, ret=%d", ret ); + } else { + dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret ); } } @@ -690,7 +860,8 @@ priv->dp_buf_len ); /* wake up line discipline */ tty = port->tty; - if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup ) + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) (tty->ldisc.write_wakeup)(tty); /* wake up other tty processes */ @@ -717,7 +888,7 @@ dbg( "digi_write_room: TOP: port=%d", port->number ); spin_unlock( &priv->dp_port_lock ); -dbg( "digi_write_room: return room=%d", room ); +dbg( "digi_write_room: port=%d, room=%d", port->number, room ); return( room ); } @@ -732,11 +903,10 @@ static int digi_chars_in_buffer( struct usb_serial_port *port ) dbg( "digi_chars_in_buffer: TOP: port=%d", port->number ); if( port->write_urb->status == -EINPROGRESS ) { -dbg( "digi_chars_in_buffer: return=%d", port->bulk_out_size ); - return( port->bulk_out_size ); - } - else { -dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, port->bulk_out_size - 2 ); + return( port->bulk_out_size - 2 ); + } else { +dbg( "digi_chars_in_buffer: port=%d, chars=%d", port->number, priv->dp_buf_len ); return( priv->dp_buf_len ); } @@ -746,61 +916,53 @@ dbg( "digi_chars_in_buffer: return=%d", priv->dp_buf_len ); static int digi_open( struct usb_serial_port *port, struct file *filp ) { + int i = 0; int ret; + unsigned char buf[32]; digi_private_t *priv = (digi_private_t *)(port->private); + struct termios not_termios; + + +dbg( "digi_open: TOP: port %d, active:%d", port->number, port->active ); + /* be sure the device is started up */ + if( digi_startup_device( port->serial ) != 0 ) + return( -ENXIO ); -dbg( "digi_open: TOP: port %d", port->number ); + MOD_INC_USE_COUNT; /* if port is already open, just return */ - /* be sure exactly one open succeeds */ + /* be sure exactly one open proceeds */ spin_lock( &priv->dp_port_lock ); - if( port->active ) { + if( port->active++ ) { + spin_unlock( &priv->dp_port_lock ); return( 0 ); } - port->active = 1; spin_unlock( &priv->dp_port_lock ); - /* start reading from the out-of-band port for the device */ - /* be sure this happens exactly once */ - spin_lock( &config_lock ); - if( !oob_read_started ) { - if( (ret=usb_submit_urb(oob_port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) for oob failed, ret=%d", - ret ); - spin_unlock( &config_lock ); - return( -ENXIO ); - } - else { -dbg( "digi_open: usb_submit_urb(read bulk) for oob succeeded" ); - oob_read_started = 1; - } - } - spin_unlock( &config_lock ); - - /* initialize port */ -dbg( "digi_open: init..." ); - /* set 9600, 8N1, DTR, RTS, RX enable, no input or output flow control */ - digi_setbaud( port, 9600 ); - digi_send_cmd( "digi_open: wordsize", port, DIGI_CMD_SET_WORD_SIZE, 2, 3 ); - digi_send_cmd( "digi_open: parity", port, DIGI_CMD_SET_PARITY, 2, 0 ); - digi_send_cmd( "digi_open: stopbits", port, DIGI_CMD_SET_STOP_BITS, 2, 0 ); - digi_send_cmd( "digi_open: DTR on", port, DIGI_CMD_SET_DTR_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RTS on", port, DIGI_CMD_SET_RTS_SIGNAL, 2, 1 ); - digi_send_cmd( "digi_open: RX enable on", port, DIGI_CMD_RECEIVE_ENABLE, 2, - 1 ); - digi_send_cmd( "digi_open: input flow control off", port, - DIGI_CMD_SET_INPUT_FLOW_CONTROL, 2, 0 ); - digi_send_cmd( "digi_open: output flow control off", port, - DIGI_CMD_SET_OUTPUT_FLOW_CONTROL, 2, 0 ); - - /* start reading from the device */ - if( (ret=usb_submit_urb(port->read_urb)) != 0 ) { - dbg( "digi_open: usb_submit_urb(read bulk) failed, ret=%d", ret ); - return( -ENXIO ); - } + /* read modem signals automatically whenever they change */ + buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[i++] = port->number; + buf[i++] = DIGI_ENABLE; + buf[i++] = 0; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_open: write oob failed, ret=%d", ret ); + + /* set termios settings */ + not_termios.c_cflag = ~port->tty->termios->c_cflag; + not_termios.c_iflag = ~port->tty->termios->c_iflag; + digi_set_termios( port, ¬_termios ); + + /* set DTR and RTS */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); -dbg( "digi_open: done" ); return( 0 ); } @@ -809,39 +971,119 @@ dbg( "digi_open: done" ); static void digi_close( struct usb_serial_port *port, struct file *filp ) { -dbg( "digi_close: TOP: port %d", port->number ); - - /* Need to change the control lines here */ - /* TODO */ -dbg( "digi_close: wanna clear DTR and RTS..." ); + int i = 0; + int ret; + unsigned char buf[32]; + digi_private_t *priv = (digi_private_t *)(port->private); -//digi_send_cmd( "digi_close DTR off", port, 6, 2, 0); // clear DTR -//digi_send_cmd( "digi_close RTS off", port, 7, 2, 0); // clear RTS -//digi_send_cmd( "digi_close RX disable", port, 10, 2, 0); // Rx Disable -digi_send_oob( "digi_close RTS off", DIGI_CMD_SET_RTS_SIGNAL, - port->number, 0, 0 ); // clear RTS -digi_send_oob( "digi_close DTR off", DIGI_CMD_SET_DTR_SIGNAL, - port->number, 0, 0 ); // clear DTR +dbg( "digi_close: TOP: port %d, active:%d", port->number, port->active ); + + /* do cleanup only after final close on this port */ + spin_lock( &priv->dp_port_lock ); + if( --port->active ) { + spin_unlock( &priv->dp_port_lock ); + MOD_DEC_USE_COUNT; + return; + } + spin_unlock( &priv->dp_port_lock ); + + /* drop DTR and RTS */ + digi_set_modem_signals( port, 0 ); + + /* disable input flow control */ + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* disable output flow control */ + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* disable reading modem signals automatically */ + buf[i++] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + /* flush fifos */ + buf[i++] = DIGI_CMD_IFLUSH_FIFO; + buf[i++] = port->number; + buf[i++] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[i++] = 0; + + /* disable receive */ + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = port->number; + buf[i++] = DIGI_DISABLE; + buf[i++] = 0; + + if( (ret=digi_write_oob( buf, i )) != 0 ) + dbg( "digi_close: write oob failed, ret=%d", ret ); + + /* wait for final commands on oob port to complete */ while( oob_port->write_urb->status == -EINPROGRESS ) { -dbg ("digi_close: waiting for final writes to complete on oob port %d...", oob_port->number ); - interruptible_sleep_on( &oob_port->write_wait ); + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); if( signal_pending(current) ) { break; } } - /* shutdown our bulk reads and writes */ + /* shutdown any outstanding bulk writes */ usb_unlink_urb (port->write_urb); - usb_unlink_urb (port->read_urb); - port->active = 0; + MOD_DEC_USE_COUNT; + +} + + +/* +* Digi Startup Device +* +* Starts reads on all ports. Must be called AFTER startup, with +* urbs initialized. Returns 0 if successful, non-zero error otherwise. +*/ + +static int digi_startup_device( struct usb_serial *serial ) +{ + + int i,ret = 0; + + + spin_lock( &startup_lock ); + + /* be sure this happens exactly once */ + if( device_startup ) { + spin_unlock( &startup_lock ); + return( 0 ); + } + + /* start reading from each bulk in endpoint for the device */ + for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + + if( (ret=usb_submit_urb(serial->port[i].read_urb)) != 0 ) { + dbg( "digi_startup_device: usb_submit_urb failed, port=%d, ret=%d", + i, ret ); + break; + } + + } + + device_startup = 1; + + spin_unlock( &startup_lock ); + + return( ret ); } -static int digi_startup (struct usb_serial *serial) +static int digi_startup( struct usb_serial *serial ) { int i; @@ -850,22 +1092,24 @@ static int digi_startup (struct usb_serial *serial) dbg( "digi_startup: TOP" ); - /* initialize config lock */ - spin_lock_init( &config_lock ); + spin_lock_init( &startup_lock ); /* allocate the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + serial->port[i].active = 0; + /* allocate private structure */ priv = serial->port[i].private = - (digi_private_t *)kmalloc( sizeof(struct digi_private), + (digi_private_t *)kmalloc( sizeof(digi_private_t), GFP_KERNEL ); if( priv == (digi_private_t *)0 ) - return( 1 ); /* error */ + return( 1 ); /* error */ /* initialize private structure */ priv->dp_buf_len = 0; + priv->dp_modem_signals = 0; spin_lock_init( &priv->dp_port_lock ); /* initialize write wait queue for this port */ @@ -876,7 +1120,7 @@ dbg( "digi_startup: TOP" ); /* initialize out of band port info */ oob_port_num = digi_acceleport_device.num_ports; oob_port = &serial->port[oob_port_num]; - oob_read_started = 0; + device_startup = 0; return( 0 ); @@ -891,10 +1135,13 @@ static void digi_shutdown( struct usb_serial *serial ) dbg( "digi_shutdown: TOP" ); - /* stop writing and reading from the out-of-band port */ - usb_unlink_urb( oob_port->write_urb ); - usb_unlink_urb( oob_port->read_urb ); - oob_read_started = 0; + /* stop reads and writes on all ports */ + for( i=0; i<digi_acceleport_device.num_ports+1; i++ ) { + usb_unlink_urb (serial->port[i].read_urb); + usb_unlink_urb (serial->port[i].write_urb); + } + + device_startup = 0; /* free the private data structures for all ports */ /* number of regular ports + 1 for the out-of-band port */ @@ -910,7 +1157,10 @@ static void digi_read_bulk_callback( struct urb *urb ) struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; struct tty_struct *tty = port->tty; - unsigned char *data = urb->transfer_buffer; + int opcode = ((unsigned char *)urb->transfer_buffer)[0]; + int len = ((unsigned char *)urb->transfer_buffer)[1]; + int status = ((unsigned char *)urb->transfer_buffer)[2]; + unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; int ret,i; @@ -918,58 +1168,108 @@ dbg( "digi_read_bulk_callback: TOP: port=%d", port->number ); /* handle oob callback */ if( port->number == oob_port_num ) { -dbg( "digi_read_bulk_callback: oob_port callback, opcode=%d, line=%d, status=%d, ret=%d", data[0], data[1], data[2], data[3] ); - if( urb->status ) { - dbg( "digi_read_bulk_callback: nonzero read bulk status on oob: %d", - urb->status ); - } - if( (ret=usb_submit_urb(urb)) != 0 ) { - dbg( "digi_read_bulk_callback: failed resubmitting oob urb, ret=%d", - ret ); - } + digi_read_oob( urb ); return; } /* sanity checks */ if( port_paranoia_check( port, "digi_read_bulk_callback" ) || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) { - return; + goto resubmit; } - /* check status */ if( urb->status ) { dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", urb->status ); - return; + goto resubmit; } -#ifdef DEBUG - if (urb->actual_length) { - printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: length=%d, data=", - urb->actual_length ); - for( i=0; i<urb->actual_length; ++i ) { - printk( "%.2x ", data[i] ); - } - printk( "\n" ); +#ifdef DEBUG_DATA +if( urb->actual_length ) { + printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=", + port->number, urb->actual_length ); + for( i=0; i<urb->actual_length; ++i ) { + printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); } + printk( "\n" ); +} #endif - /* Digi read packets are: */ - /* 0 1 2 3 4 ... 3+length-1 == 2+length*/ - /* opcode, length, status, data[0], data[1]...data[length-2] */ - if( urb->actual_length > 3 ) { - for( i=3; i<2+data[1]; ++i ) { + if( urb->actual_length != len + 2 ) + err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", port->number, opcode, len, urb->actual_length, status ); + + /* receive data */ + if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) { + len = MIN( len, urb->actual_length-3 ); + for( i=0; i<len; ++i ) { tty_insert_flip_char(tty, data[i], 0); } tty_flip_buffer_push(tty); } - /* Continue trying to read */ + /* continue read */ +resubmit: if( (ret=usb_submit_urb(urb)) != 0 ) - dbg( "digi_read_bulk_callback: failed resubmitting read urb, ret=%d", + dbg( "digi_read_bulk_callback: failed resubmitting urb, ret=%d", ret ); } -#endif /* CONFIG_USB_SERIAL_DIGI_ACCELEPORT */ + +static void digi_read_oob( struct urb *urb ) +{ + + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + digi_private_t *priv; + int oob_opcode = ((unsigned char *)urb->transfer_buffer)[0]; + int oob_line = ((unsigned char *)urb->transfer_buffer)[1]; + int oob_status = ((unsigned char *)urb->transfer_buffer)[2]; + int oob_ret = ((unsigned char *)urb->transfer_buffer)[3]; + int ret; + + +dbg( "digi_read_oob: opcode=%d, line=%d, status=%d, ret=%d", oob_opcode, oob_line, oob_status, oob_ret ); + + if( urb->status ) { + dbg( "digi_read_oob: nonzero read bulk status on oob: %d", + urb->status ); + goto resubmit; + } + + if( oob_opcode == DIGI_CMD_READ_INPUT_SIGNALS && oob_status == 0 ) { + + priv = serial->port[oob_line].private; + + spin_lock( &priv->dp_port_lock ); + + /* convert from digi flags to termiox flags */ + if( oob_ret & DIGI_READ_INPUT_SIGNALS_CTS ) + priv->dp_modem_signals |= TIOCM_CTS; + else + priv->dp_modem_signals &= ~TIOCM_CTS; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_DSR ) + priv->dp_modem_signals |= TIOCM_DSR; + else + priv->dp_modem_signals &= ~TIOCM_DSR; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_RI ) + priv->dp_modem_signals |= TIOCM_RI; + else + priv->dp_modem_signals &= ~TIOCM_RI; + if( oob_ret & DIGI_READ_INPUT_SIGNALS_DCD ) + priv->dp_modem_signals |= TIOCM_CD; + else + priv->dp_modem_signals &= ~TIOCM_CD; + + spin_unlock( &priv->dp_port_lock ); + + } + +resubmit: + if( (ret=usb_submit_urb(urb)) != 0 ) { + dbg( "digi_read_oob: failed resubmitting oob urb, ret=%d", + ret ); + } + +} diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index cdad3cabe..8aec73569 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -32,9 +32,6 @@ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_FTDI_SIO - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -723,6 +720,3 @@ static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, uns return 0; } /* ftdi_sio_ioctl */ -#endif /* CONFIG_USB_SERIAL_FTDI_SIO */ - - diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index f91e1b592..c18137ffa 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -18,9 +18,6 @@ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -700,4 +697,3 @@ struct usb_serial_device_type keyspan_pda_device = { shutdown: keyspan_pda_shutdown, }; -#endif /* CONFIG_USB_SERIAL_KEYSPAN_PDA */ diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 9eb6767f0..2e5e50849 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -13,9 +13,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_OMNINET - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -336,6 +333,3 @@ static void omninet_write_bulk_callback (struct urb *urb) return; } -#endif /* CONFIG_USB_SERIAL_OMNINET */ - - diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index 9fe65ae8d..b2e18bd8e 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,10 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (05/22/2000) gkh + * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be + * removed from the individual device source files. + * * (05/03/2000) gkh * Added the Digi Acceleport driver from Al Borchers and Peter Berger. * diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 979a56f01..5cfb4c42a 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -20,9 +20,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_VISOR - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -207,7 +204,3 @@ static int visor_startup (struct usb_serial *serial) return (0); } - -#endif /* CONFIG_USB_SERIAL_VISOR*/ - - diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 58f761fbf..c168efa02 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -21,9 +21,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_SERIAL_WHITEHEAT - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/signal.h> @@ -407,6 +404,3 @@ static void whiteheat_shutdown (struct usb_serial *serial) return; } -#endif /* CONFIG_USB_SERIAL_WHITEHEAT */ - - diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 2eaab47ef..a3d8d3d42 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -821,6 +821,7 @@ status_phase: /* Control status phase */ status = uhci_status_bits(td->status); +#ifdef I_HAVE_BUGGY_APC_BACKUPS /* APC BackUPS Pro kludge */ /* It tries to send all of the descriptor instead of the amount */ /* we requested */ @@ -828,6 +829,7 @@ status_phase: status & TD_CTRL_ACTIVE && status & TD_CTRL_NAK) return 0; +#endif if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -2325,31 +2327,30 @@ static int found_uhci(struct pci_dev *dev) { int i; + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + if (pci_enable_device(dev) < 0) + return -1; + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + return -1; + } + /* Search for the IO base address.. */ for (i = 0; i < 6; i++) { - unsigned int io_addr = dev->resource[i].start; - unsigned int io_size = - dev->resource[i].end - dev->resource[i].start + 1; + unsigned int io_addr = pci_resource_start(dev, i); + unsigned int io_size = pci_resource_len(dev, i); /* IO address? */ - if (!(dev->resource[i].flags & IORESOURCE_IO)) + if (!(pci_resource_flags(dev, i) & IORESOURCE_IO)) continue; /* Is it already in use? */ if (check_region(io_addr, io_size)) break; - if (!dev->irq) { - err("found UHCI device with no IRQ assigned. check BIOS settings!"); - continue; - } - - /* disable legacy emulation */ - pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); - - if (pci_enable_device(dev) < 0) - continue; - return setup_uhci(dev, dev->irq, io_addr, io_size); } diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c index 50388dccf..a37e805f0 100644 --- a/drivers/usb/usb-ohci.c +++ b/drivers/usb/usb-ohci.c @@ -92,7 +92,6 @@ static void urb_rm_priv (urb_t * urb) kfree (urb->hcpriv); urb->hcpriv = NULL; - wake_up (&op_wakeup); } /*-------------------------------------------------------------------------*/ @@ -493,16 +492,16 @@ static int sohci_submit_urb (urb_t * urb) urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): (le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff; } + urb->status = USB_ST_URB_PENDING; + urb->actual_length = 0; if (ed->state != ED_OPER) /* link the ed into a chain if is not already */ ep_link (ohci, ed); + urb->status = USB_ST_URB_PENDING; td_submit_urb (urb); /* fill the TDs and link it to the ed */ spin_unlock_irqrestore (&usb_ed_lock, flags); - - urb->status = USB_ST_URB_PENDING; - // queue_urb(s, &urb->urb_list); return 0; } @@ -529,6 +528,8 @@ static int sohci_unlink_urb (urb_t * urb) urb_print (urb, "UNLINK", 1); #endif + usb_dec_dev_use (urb->dev); + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) return rh_unlink_urb (urb); /* a request to the virtual root hub */ @@ -546,19 +547,23 @@ static int sohci_unlink_urb (urb_t * urb) ep_rm_ed (urb->dev, urb_priv->ed); urb_priv->ed->state |= ED_URB_DEL; spin_unlock_irqrestore (&usb_ed_lock, flags); - - add_wait_queue (&op_wakeup, &wait); - current->state = TASK_UNINTERRUPTIBLE; - if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ - err("unlink URB timeout!"); - remove_wait_queue (&op_wakeup, &wait); - } else + if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { + add_wait_queue (&op_wakeup, &wait); + current->state = TASK_UNINTERRUPTIBLE; + if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ + err("unlink URB timeout!"); + remove_wait_queue (&op_wakeup, &wait); + urb->status = -ENOENT; + } else + urb->status = -EINPROGRESS; + } else { urb_rm_priv (urb); - - urb->status = -ENOENT; // mark urb as killed - if (urb->complete) - urb->complete ((struct urb *) urb); - usb_dec_dev_use (urb->dev); + if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { + urb->complete (urb); + urb->status = 0; + } else + urb->status = -ENOENT; + } } return 0; } @@ -967,7 +972,7 @@ static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) /* prepare a TD */ -static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int type, int index) +static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index) { volatile td_t * td, * td_pt; urb_priv_t * urb_priv = urb->hcpriv; @@ -984,7 +989,6 @@ static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int t td->index = index; td->urb = urb; td->hwINFO = cpu_to_le32 (info); - td->type = type; if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) { td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data)) & 0xFFFFF000); @@ -1031,12 +1035,12 @@ static void td_submit_urb (urb_t * urb) info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; while(data_len > 4096) { - td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); data += 4096; data_len -= 4096; cnt++; } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; - td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); cnt++; writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ break; @@ -1044,20 +1048,20 @@ static void td_submit_urb (urb_t * urb) case PIPE_INTERRUPT: info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; - td_fill (info, data, data_len, urb, ST_ADDR | ADD_LEN, cnt++); + td_fill (info, data, data_len, urb, cnt++); break; case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (info, ctrl, 8, urb, ST_ADDR, cnt++); + td_fill (info, ctrl, 8, urb, cnt++); if (data_len > 0) { info = usb_pipeout (urb->pipe)? TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; - td_fill (info, data, data_len, urb, ADD_LEN, cnt++); + td_fill (info, data, data_len, urb, cnt++); } info = usb_pipeout (urb->pipe)? TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; - td_fill (info, NULL, 0, urb, 0, cnt++); + td_fill (info, NULL, 0, urb, cnt++); writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ break; @@ -1065,7 +1069,7 @@ static void td_submit_urb (urb_t * urb) for (cnt = 0; cnt < urb->number_of_packets; cnt++) { td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), (__u8 *) data + urb->iso_frame_desc[cnt].offset, - urb->iso_frame_desc[cnt].length, urb, (cnt? 0: ST_ADDR) | ADD_LEN, cnt); + urb->iso_frame_desc[cnt].length, urb, cnt); } break; } @@ -1076,7 +1080,55 @@ static void td_submit_urb (urb_t * urb) /*-------------------------------------------------------------------------* * Done List handling functions *-------------------------------------------------------------------------*/ - + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + __u16 tdPSW; + urb_t * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + int dlen = 0; + int cc = 0; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (tdINFO & TD_ISO) { + tdPSW = le16_to_cpu (td->hwPSW[0]); + cc = (tdPSW >> 12) & 0xF; + if (cc < 0xE) { + if (usb_pipeout(urb->pipe)) { + dlen = urb->iso_frame_desc[td->index].length; + } else { + dlen = tdPSW & 0x3ff; + } + urb->actual_length += dlen; + urb->iso_frame_desc[td->index].actual_length = dlen; + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + urb->iso_frame_desc[td->index].status = cc_to_error[cc]; + } + } else { /* BULK, INT, CONTROL DATA */ + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1; + else + urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + /* replies to the request have to be on a FIFO basis so * we reverse the reversed done-list */ @@ -1130,6 +1182,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) unsigned long flags; ed_t * ed; __u32 edINFO; + __u32 tdINFO; td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; __u32 * td_p; int ctrl = 0, bulk = 0; @@ -1141,16 +1194,25 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); edINFO = le32_to_cpup (&ed->hwINFO); td_p = &ed->hwHeadP; - + for (td = tdHeadP; td != tdTailP; td = td_next) { urb_t * urb = td->urb; urb_priv_t * urb_priv = td->urb->hcpriv; td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0); if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { + tdINFO = le32_to_cpup (&td->hwINFO); + if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td); *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); if(++ (urb_priv->td_cnt) == urb_priv->length) urb_rm_priv (urb); + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + usb_dec_dev_use (urb->dev); + urb->status = -ECONNRESET; + urb->complete (urb); + } else { + wake_up (&op_wakeup); + } } else { td_p = &td->hwNextTD; } @@ -1167,7 +1229,7 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) } else { ed->state &= ~ED_URB_DEL; - ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); + ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); } if ((ed->type & 3) == CTRL) ctrl |= 1; @@ -1185,6 +1247,8 @@ static void dl_del_list (ohci_t * ohci, unsigned int frame) spin_unlock_irqrestore (&usb_ed_lock, flags); } + + /*-------------------------------------------------------------------------*/ /* td done list */ @@ -1193,12 +1257,11 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list) { td_t * td_list_next = NULL; ed_t * ed; - int dlen = 0; int cc = 0; urb_t * urb; urb_priv_t * urb_priv; - __u32 tdINFO, tdBE, tdCBP, edHeadP, edTailP; - __u16 tdPSW; + __u32 tdINFO, edHeadP, edTailP; + unsigned long flags; while (td_list) { @@ -1207,40 +1270,11 @@ static void dl_done_list (ohci_t * ohci, td_t * td_list) urb = td_list->urb; urb_priv = urb->hcpriv; tdINFO = le32_to_cpup (&td_list->hwINFO); - tdBE = le32_to_cpup (&td_list->hwBE); - tdCBP = le32_to_cpup (&td_list->hwCBP); ed = td_list->ed; - if (td_list->type & ST_ADDR) - urb->actual_length = 0; - - if (td_list->type & ADD_LEN) { /* accumulate length of multi td transfers */ - if (tdINFO & TD_ISO) { - tdPSW = le16_to_cpu (td_list->hwPSW[0]); - cc = (tdPSW >> 12) & 0xF; - if (cc < 0xE) { - if (usb_pipeout(urb->pipe)) { - dlen = urb->iso_frame_desc[td_list->index].length; - } else { - dlen = tdPSW & 0x3ff; - } - urb->actual_length += dlen; - urb->iso_frame_desc[td_list->index].actual_length = dlen; - if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) - cc = TD_CC_NOERROR; - - urb->iso_frame_desc[td_list->index].status = cc_to_error[cc]; - } - } else { - if (tdBE != 0) { - if (td_list->hwCBP == 0) - urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1; - else - urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; - } - } - } + dl_transfer_length(td_list); + /* error code of transfer */ cc = TD_CC_GET (tdINFO); if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); @@ -1936,11 +1970,11 @@ static int hc_start_ohci (struct pci_dev * dev) { unsigned long mem_base; - mem_base = dev->resource[0].start; if (pci_enable_device(dev) < 0) return -ENODEV; pci_set_master (dev); + mem_base = dev->resource[0].start; mem_base = (unsigned long) ioremap_nocache (mem_base, 4096); if (!mem_base) { diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h index e854e0c1d..3ac5dafb2 100644 --- a/drivers/usb/usb-ohci.h +++ b/drivers/usb/usb-ohci.h @@ -432,7 +432,7 @@ static int ep_unlink(ohci_t * ohci, ed_t * ed); static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load); static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); /* td */ -static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int type, int index); +static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index); static void td_submit_urb(urb_t * urb); /* root hub */ static int rh_submit_urb(urb_t * urb); diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index 42157dbab..b9cead4fe 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -1,7 +1,10 @@ /* Driver for USB Mass Storage compliant devices * - * (c) 1999 Michael Gee (michael@linuxspecific.com) - * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * * This driver is based on the 'USB Mass Storage Class' document. This * describes in detail the protocol used to communicate with such @@ -16,6 +19,9 @@ * * Also, for certain devices, the interrupt endpoint is used to convey * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. */ #include <linux/module.h> @@ -111,10 +117,15 @@ struct us_data { struct semaphore ip_waitq; /* for CBI interrupts */ int ip_wanted; /* is an IRQ expected? */ + /* interrupt communications data */ struct semaphore irq_urb_sem; /* to protect irq_urb */ struct urb *irq_urb; /* for USB int requests */ unsigned char irqbuf[2]; /* buffer for USB IRQ */ + /* control and bulk communications data */ + struct semaphore current_urb_sem; /* to protect irq_urb */ + struct urb *current_urb; /* non-int USB requests */ + /* mutual exclusion structures */ struct semaphore notify; /* thread begin/end */ struct semaphore sleeper; /* to sleep the thread on */ @@ -147,6 +158,155 @@ static struct usb_driver storage_driver = { * Data transfer routines ***********************************************************************/ +/* This is the completion handler which will wake us up when an URB + * completes. + */ +static void usb_stor_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +} + +/* This is our function to emulate usb_control_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + devrequest *dr; + + /* allocate the device request structure */ + dr = kmalloc(sizeof(devrequest), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + /* fill in the structure */ + dr->requesttype = requesttype; + dr->request = request; + dr->value = cpu_to_le16(value); + dr->index = cpu_to_le16(index); + dr->length = cpu_to_le16(size); + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) dr, data, size, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + kfree(dr); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_control_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred if no error*/ + if (status >= 0) + status = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + kfree(dr); + return status; +} + +/* This is our function to emulate usb_bulk_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, + unsigned int len, unsigned int *act_len) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_bulk_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred */ + *act_len = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + return status; +} + /* * Transfer one SCSI scatter-gather buffer via bulk transfer * @@ -158,26 +318,33 @@ static struct usb_driver storage_driver = { * timeout limit. Thus we don't have to worry about it for individual * packets. */ -static int us_transfer_partial(struct us_data *us, int pipe, - char *buf, int length) +static int us_transfer_partial(struct us_data *us, char *buf, int length) { int result; int partial; + int pipe; + + /* calculate the appropriate pipe information */ + if (US_DIRECTION(us->srb->cmnd[0])) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); /* transfer the data */ - US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length); - result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, 5*HZ); - US_DEBUGP("bulk_msg returned %d xferred %d/%d\n", + US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length); + result = usb_stor_bulk_msg(us, buf, pipe, length, &partial); + US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n", result, partial, length); - + /* if we stall, we need to clear it before we go on */ if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_clear_halt(us->pusb_dev, pipe); } - + /* did we send all the data? */ if (partial == length) { + US_DEBUGP("us_transfer_partial(): transfer complete\n"); return US_BULK_TRANSFER_GOOD; } @@ -186,7 +353,17 @@ static int us_transfer_partial(struct us_data *us, int pipe, /* NAK - that means we've retried a few times allready */ if (result == -ETIMEDOUT) { US_DEBUGP("us_transfer_partial(): device NAKed\n"); + return US_BULK_TRANSFER_FAILED; } + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + /* the catch-all case */ + US_DEBUGP("us_transfer_partial(): unknown error\n"); return US_BULK_TRANSFER_FAILED; } @@ -203,21 +380,12 @@ static int us_transfer_partial(struct us_data *us, int pipe, * function simply determines if we're going to use scatter-gather or not, * and acts appropriately. For now, it also re-interprets the error codes. */ -static void us_transfer(Scsi_Cmnd *srb, int dir_in) +static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in) { - struct us_data *us; int i; int result = -1; - unsigned int pipe; struct scatterlist *sg; - /* calculate the appropriate pipe information */ - us = (struct us_data*) srb->host_scribble; - if (dir_in) - pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); - else - pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); - /* are we scatter-gathering? */ if (srb->use_sg) { @@ -226,7 +394,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in) */ sg = (struct scatterlist *) srb->request_buffer; for (i = 0; i < srb->use_sg; i++) { - result = us_transfer_partial(us, pipe, sg[i].address, + result = us_transfer_partial(us, sg[i].address, sg[i].length); if (result) break; @@ -234,7 +402,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in) } else /* no scatter-gather, just make the request */ - result = us_transfer_partial(us, pipe, srb->request_buffer, + result = us_transfer_partial(us, srb->request_buffer, srb->request_bufflen); /* return the result in the data structure itself */ @@ -433,6 +601,8 @@ static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us) /* * Control/Bulk/Interrupt transport */ + +/* The interrupt handler for CBI devices */ static void CBI_irq(struct urb *urb) { struct us_data *us = (struct us_data *)urb->context; @@ -465,13 +635,13 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* COMMAND STAGE */ /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ - US_DEBUGP("Call to usb_control_msg() returned %d\n", result); + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { /* STALL must be cleared when they are detected */ if (result == -EPIPE) { @@ -493,7 +663,7 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (us_transfer_length(srb, us)) { - us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); US_DEBUGP("CBI data stage result is 0x%x\n", srb->result); } @@ -561,15 +731,14 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* COMMAND STAGE */ /* let's send the command via the control pipe */ - result = usb_control_msg(us->pusb_dev, - usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, - USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, - us->ifnum, srb->cmnd, srb->cmd_len, HZ*5); + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); if (result < 0) { - US_DEBUGP("Call to usb_control_msg() returned %d\n", result); - /* a stall is a fatal condition from the device */ if (result == -EPIPE) { US_DEBUGP("-- Stall on control pipe. Clearing\n"); @@ -587,7 +756,7 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* DATA STAGE */ /* transfer the data payload for this command, if one exists*/ if (us_transfer_length(srb, us)) { - us_transfer(srb, US_DIRECTION(srb->cmnd[0])); + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); US_DEBUGP("CB data stage result is 0x%x\n", srb->result); } @@ -602,6 +771,39 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) /* * Bulk only transport */ + +/* Determine what the maximum LUN supported is */ +static int Bulk_max_lun(struct us_data *us) +{ + unsigned char data; + int result; + int pipe; + + /* issue the command */ + pipe = usb_rcvctrlpipe(us->pusb_dev, 0); + result = usb_control_msg(us->pusb_dev, pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, &data, sizeof(data), HZ); + + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", + result, data); + + /* if we have a successful request, return the result */ + if (!result) + return data; + + /* if we get a STALL, clear the stall */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* return the default -- no LUNs */ + return 0; +} + static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) { struct bulk_cb_wrap bcb; @@ -629,8 +831,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n", le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, bcb.DataTransferLength, bcb.Flags, bcb.Length); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcb, - US_BULK_CB_WRAP_LEN, &partial, HZ*5); + result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, + &partial); US_DEBUGP("Bulk command transfer result=%d\n", result); /* if we stall, we need to clear it before we go on */ @@ -643,7 +845,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) if (result == 0) { /* send/receive data payload, if there is any */ if (bcb.DataTransferLength) { - us_transfer(srb, bcb.Flags); + us_transfer(srb, us, bcb.Flags); US_DEBUGP("Bulk data transfer result 0x%x\n", srb->result); } @@ -658,8 +860,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* get CSW for device status */ US_DEBUGP("Attempting to get CSW...\n"); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, - US_BULK_CS_WRAP_LEN, &partial, HZ*2); + result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, + &partial); /* did the attempt to read the CSW fail? */ if (result == -EPIPE) { @@ -668,8 +870,8 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) /* get the status again */ US_DEBUGP("Attempting to get CSW (2nd try)...\n"); - result = usb_bulk_msg(us->pusb_dev, pipe, &bcs, - US_BULK_CS_WRAP_LEN, &partial, HZ*2); + result = usb_stor_bulk_msg(us, &bcs, pipe, + US_BULK_CS_WRAP_LEN, &partial); /* if it fails again, we need a reset and return an error*/ if (result == -EPIPE) { @@ -1025,9 +1227,9 @@ static int Bulk_reset(struct us_data *us) result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), - US_BULK_RESET, + US_BULK_RESET_REQUEST, USB_TYPE_CLASS | USB_RECIP_INTERFACE, - US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5); + 0, us->ifnum, NULL, 0, HZ*5); if (result < 0) US_DEBUGP("Bulk hard reset failed %d\n", result); @@ -1049,7 +1251,7 @@ static int Bulk_reset(struct us_data *us) static const char* us_info(struct Scsi_Host *host) { - return "SCSI emulation for USB Mass Storage devices\n"; + return "SCSI emulation for USB Mass Storage devices"; } /* detect a virtual adapter (always works) */ @@ -1109,6 +1311,7 @@ static int us_release(struct Scsi_Host *psh) /* free the data structure we were using */ US_DEBUGP("-- freeing private host data structure\n"); + kfree(us->current_urb); kfree(us); (struct us_data*)psh->hostdata[0] = NULL; @@ -1289,22 +1492,11 @@ static Scsi_Host_Template my_host_template = { }; static unsigned char sense_notready[] = { - 0x70, /* current error */ - 0x00, - 0x02, /* not ready */ - 0x00, - 0x00, - 0x0a, /* additional length */ - 0x00, - 0x00, - 0x00, - 0x00, - 0x04, /* not ready */ - 0x03, /* manual intervention */ - 0x00, - 0x00, - 0x00, - 0x00 + [0] = 0x70, /* current error */ + [2] = 0x02, /* not ready */ + [5] = 0x0a, /* additional length */ + [10] = 0x04, /* not ready */ + [11] = 0x03 /* manual intervention */ }; static int usb_stor_control_thread(void * __us) @@ -1429,25 +1621,31 @@ static int usb_stor_control_thread(void * __us) return 0; } +/* This is the list of devices we recognize, along with their flag data */ static struct us_unusual_dev us_unusual_dev_list[] = { + { 0x03f0, 0x0107, 0x0200, + "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, + "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, + "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, + "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, { 0x057b, 0x0000, 0x0114, - "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN }, + "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, { 0x059b, 0x0030, 0x0100, - "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN }, + "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, + "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK, + US_FL_ALT_LENGTH}, { 0x0781, 0x0001, 0x0200, - "Sandisk ImageMate (w/eject button)", US_SC_SCSI, US_PR_CB, - US_FL_SINGLE_LUN | US_FL_START_STOP }, + "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, + US_FL_SINGLE_LUN | US_FL_START_STOP}, { 0x0781, 0x0002, 0x0009, - "** SECRET DEVICE **", US_SC_SCSI, US_PR_BULK, - US_FL_SINGLE_LUN | US_FL_IGNORE_SER }, + "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, + US_FL_SINGLE_LUN | US_FL_IGNORE_SER}, { 0x07af, 0x0005, 0x0100, "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0002, 0x0100, - "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0006, 0x0100, - "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x03f0, 0x0107, 0x0200, - "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, { 0x0000, 0x0000, 0x0, "", 0, 0, 0} }; @@ -1713,12 +1911,20 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } memset(ss, 0, sizeof(struct us_data)); + /* allocate the URB we're going to use */ + ss->current_urb = usb_alloc_urb(0); + if (!ss->current_urb) { + kfree(ss); + return NULL; + } + /* Initialize the mutexes only when the struct is new */ init_MUTEX_LOCKED(&(ss->sleeper)); init_MUTEX_LOCKED(&(ss->notify)); init_MUTEX_LOCKED(&(ss->ip_waitq)); init_MUTEX(&(ss->queue_exclusion)); init_MUTEX(&(ss->irq_urb_sem)); + init_MUTEX(&(ss->current_urb_sem)); init_MUTEX(&(ss->dev_semaphore)); /* copy over the subclass and protocol data */ @@ -1774,11 +1980,14 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->transport_name = "Bulk"; ss->transport = Bulk_transport; ss->transport_reset = Bulk_reset; + /* FIXME: for testing purposes only */ + Bulk_max_lun(ss); break; default: ss->transport_name = "Unknown"; up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1802,6 +2011,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n"); US_DEBUGP("if you see this message.\n"); up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1824,6 +2034,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) default: ss->protocol_name = "Unknown"; up(&us_list_semaphore); + kfree(ss->current_urb); kfree(ss); return NULL; break; @@ -1859,6 +2070,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) if (ss->pid < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); + kfree(ss->current_urb); kfree(ss); return NULL; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 0d5152e36..6e54b059b 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -77,16 +77,17 @@ struct bulk_cs_wrap { #define US_BULK_STAT_FAIL 1 #define US_BULK_STAT_PHASE 2 -#define US_BULK_RESET 0xff -#define US_BULK_RESET_SOFT 1 -#define US_BULK_RESET_HARD 0 +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe /* * us_bulk_transfer() return codes */ -#define US_BULK_TRANSFER_GOOD 0 -#define US_BULK_TRANSFER_SHORT 1 -#define US_BULK_TRANSFER_FAILED 2 +#define US_BULK_TRANSFER_GOOD 0 /* good transfer */ +#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */ +#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */ +#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */ /* * Transport return codes @@ -95,6 +96,7 @@ struct bulk_cs_wrap { #define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ #define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ #define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ +#define USB_STOR_TRANSPORT_ABORTED 3 /* Transport aborted */ /* * CBI accept device specific command diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 8a3318612..f2c51f205 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -12,7 +12,7 @@ * (C) Copyright 1999 Johannes Erdfelt * (C) Copyright 1999 Randy Dunlap * - * $Id: usb-uhci.c,v 1.228 2000/04/02 19:55:51 acher Exp $ + * $Id: usb-uhci.c,v 1.231 2000/05/13 15:34:17 acher Exp $ */ #include <linux/config.h> @@ -48,7 +48,7 @@ /* This enables an extra UHCI slab for memory debugging */ #define DEBUG_SLAB -#define VERSTR "$Revision: 1.228 $ time " __TIME__ " " __DATE__ +#define VERSTR "$Revision: 1.231 $ time " __TIME__ " " __DATE__ #include <linux/usb.h> #include "usb-uhci.h" @@ -109,10 +109,10 @@ void clean_descs(uhci_t *s, int force) while (q != &s->free_desc) { qh = list_entry (q, uhci_desc_t, horizontal); + q=qh->horizontal.prev; + if ((qh->last_used!=now) || force) delete_qh(s,qh); - - q=qh->horizontal.prev; } } /*-------------------------------------------------------------------*/ @@ -1142,6 +1142,12 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) urb->status = -ENOENT; // now the urb is really dead + switch (usb_pipetype (pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step2(s, urb_priv); + break; + } usb_dec_dev_use (dev); #ifdef DEBUG_SLAB @@ -1149,12 +1155,7 @@ _static void uhci_cleanup_unlink(uhci_t *s, int force) #else kfree (urb_priv); #endif - switch (usb_pipetype (pipe)) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - uhci_clean_iso_step2(s, urb_priv); - break; - } + list_del (&urb->urb_list); } } @@ -1168,7 +1169,9 @@ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb) async_dbg("unlink_urb_async called %p",urb); - if (urb->status == -EINPROGRESS) { + if ((urb->status == -EINPROGRESS) || + ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) + { ((urb_priv_t*)urb->hcpriv)->started = ~0; dequeue_urb (s, urb); @@ -1560,7 +1563,7 @@ _static int uhci_submit_urb (urb_t *urb) urb->hcpriv = urb_priv; INIT_LIST_HEAD (&urb_priv->desc_list); - urb_priv->short_control_packet = 0; + urb_priv->flags = 0; dbg("submit_urb: scheduling %p", urb); urb_priv->next_queued_urb = NULL; urb_priv->prev_queued_urb = NULL; @@ -2151,7 +2154,7 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode) status stage is completed */ - if (urb_priv->short_control_packet && + if (urb_priv->flags && ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) goto transfer_finished; @@ -2199,7 +2202,7 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode) dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); //uhci_show_td (desc); //uhci_show_td (last_desc); - urb_priv->short_control_packet=1; + urb_priv->flags = 1; // mark as short control packet return 0; } } @@ -2280,35 +2283,43 @@ _static int process_interrupt (uhci_t *s, urb_t *urb) if (urb->complete) { //dbg("process_interrupt: calling completion, status %i",status); urb->status = status; - + ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion + spin_unlock(&s->urb_list_lock); urb->complete ((struct urb *) urb); spin_lock(&s->urb_list_lock); - - urb->status = -EINPROGRESS; + + ((urb_priv_t*)urb->hcpriv)->flags=0; } + + if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && + (urb->status != -ENOENT)) { + + urb->status = -EINPROGRESS; - // Recycle INT-TD if interval!=0, else mark TD as one-shot - if (urb->interval) { - - desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); - if (status==0) { - ((urb_priv_t*)urb->hcpriv)->started=jiffies; - desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); - usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); - } else { - desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + // Recycle INT-TD if interval!=0, else mark TD as one-shot + if (urb->interval) { + + desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); + if (status==0) { + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } else { + desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + } + desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + mb(); + } + else { + uhci_unlink_urb_async(s, urb); + desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD } - desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | - (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); - mb(); - } - else { - desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD } } @@ -2334,7 +2345,7 @@ _static int process_iso (uhci_t *s, urb_t *urb, int mode) dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), urb->number_of_packets,mode,desc->hw.td.status); - for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) { + for (i = 0; p != &urb_priv->desc_list; i++) { desc = list_entry (p, uhci_desc_t, desc_list); //uhci_show_td(desc); @@ -2378,8 +2389,9 @@ _static int process_iso (uhci_t *s, urb_t *urb, int mode) dbg("process_iso: %i: len:%d %08x status:%x", i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status); - delete_desc (desc); list_del (p); + p = p->next; + delete_desc (desc); } dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); @@ -2824,7 +2836,6 @@ int __init uhci_init (void) if (type != 0) continue; - if (pci_enable_device (dev) < 0) continue; diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h index 3c5717d1e..67eb4d210 100644 --- a/drivers/usb/usb-uhci.h +++ b/drivers/usb/usb-uhci.h @@ -2,7 +2,7 @@ #define __LINUX_UHCI_H /* - $Id: usb-uhci.h,v 1.54 2000/04/02 19:55:53 acher Exp $ + $Id: usb-uhci.h,v 1.55 2000/05/13 12:50:30 acher Exp $ */ #define MODNAME "usb-uhci" #define UHCI_LATENCY_TIMER 0 @@ -160,7 +160,7 @@ typedef struct { uhci_desc_t *bottom_qh; uhci_desc_t *next_qh; // next helper QH char use_loop; - char short_control_packet; + char flags; } urb_priv_t, *purb_priv_t; struct virt_root_hub { diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c index cb77f0717..3fa4a2d35 100644 --- a/drivers/usb/wacom.c +++ b/drivers/usb/wacom.c @@ -104,8 +104,8 @@ struct wacom_features { int distance_max; void (*irq)(struct urb *urb); unsigned long evbit; - unsigned long relbit; unsigned long absbit; + unsigned long relbit; unsigned long btnbit; unsigned long digibit; }; |