diff -Nru a/CREDITS b/CREDITS --- a/CREDITS 2005-02-04 22:22:37 -08:00 +++ b/CREDITS 2005-02-04 22:22:37 -08:00 @@ -826,6 +826,11 @@ W: http://www.fsmlabs.com/linuxppcbk.html D: PowerPC +N: Daniel Drake +E: dsd@gentoo.org +D: USBAT02 CompactFlash support in usb-storage +S: UK + N: Oleg Drokin E: green@ccssu.crimea.ua W: http://www.ccssu.crimea.ua/~green diff -Nru a/Documentation/usb/sn9c102.txt b/Documentation/usb/sn9c102.txt --- a/Documentation/usb/sn9c102.txt 2005-02-04 22:22:37 -08:00 +++ b/Documentation/usb/sn9c102.txt 2005-02-04 22:22:37 -08:00 @@ -210,8 +210,8 @@ SN9C10x bridge, while the other two control the sensor chip. "reg" and "i2c_reg" hold the values of the current register index where the following reading/writing operations are addressed at through "val" and "i2c_val". Their -use is not intended for end-users. Note that "i2c_reg" and "i2c_val" won't be -created if the sensor does not actually support the standard I2C protocol or +use is not intended for end-users. Note that "i2c_reg" and "i2c_val" will not +be created if the sensor does not actually support the standard I2C protocol or its registers are not 8-bit long. Also, remember that you must be logged in as root before writing to them. @@ -341,15 +341,8 @@ All the available control settings of each image sensor are supported through the V4L2 interface. -If you think your camera is based on the above hardware and is not actually -listed in the above table, you may try to add the specific USB VendorID and -ProductID identifiers to the sn9c102_id_table[] in the file "sn9c102_sensor.h"; -then compile, load the module again and look at the kernel output. -If this works, please send an email to the author reporting the kernel -messages, so that a new entry in the list of supported devices can be added. - Donations of new models for further testing and support would be much -appreciated. Non-available hardware won't be supported by the author of this +appreciated. Non-available hardware will not be supported by the author of this driver. diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS 2005-02-04 22:22:37 -08:00 +++ b/MAINTAINERS 2005-02-04 22:22:37 -08:00 @@ -2028,6 +2028,12 @@ W: http://www.winischhofer.net/linuxsisvga.shtml S: Maintained +SIS USB2VGA DRIVER +P: Thomas Winischhofer +M: thomas@winischhofer.net +W: http://www.winischhofer.at/linuxsisusbvga.shtml +S: Maintained + SMSC47M1 HARDWARE MONITOR DRIVER P: Jean Delvare M: khali@linux-fr.org diff -Nru a/drivers/block/ub.c b/drivers/block/ub.c --- a/drivers/block/ub.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/block/ub.c 2005-02-04 22:22:37 -08:00 @@ -300,6 +300,11 @@ /* */ +static int ub_bd_rq_fn_1(struct ub_dev *sc, struct request *rq); +static int ub_cmd_build_block(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + struct request *rq); +static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + struct request *rq); static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_end_rq(struct request *rq, int uptodate); static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd); @@ -591,40 +596,73 @@ * The request function is our main entry point */ -static inline int ub_bd_rq_fn_1(request_queue_t *q) +static void ub_bd_rq_fn(request_queue_t *q) { -#if 0 - int writing = 0, pci_dir, i, n_elem; - u32 tmp; - unsigned int msg_size; -#endif struct ub_dev *sc = q->queuedata; struct request *rq; -#if 0 /* We use rq->buffer for now */ - struct scatterlist *sg; - int n_elem; -#endif + + while ((rq = elv_next_request(q)) != NULL) { + if (ub_bd_rq_fn_1(sc, rq) != 0) { + blk_stop_queue(q); + break; + } + } +} + +static int ub_bd_rq_fn_1(struct ub_dev *sc, struct request *rq) +{ struct ub_scsi_cmd *cmd; - int ub_dir; - unsigned int block, nblks; int rc; - if ((rq = elv_next_request(q)) == NULL) - return 1; - if (atomic_read(&sc->poison) || sc->changed) { blkdev_dequeue_request(rq); ub_end_rq(rq, 0); return 0; } - if ((cmd = ub_get_cmd(sc)) == NULL) { - blk_stop_queue(q); - return 1; - } + if ((cmd = ub_get_cmd(sc)) == NULL) + return -1; + memset(cmd, 0, sizeof(struct ub_scsi_cmd)); blkdev_dequeue_request(rq); + if (blk_pc_request(rq)) { + rc = ub_cmd_build_packet(sc, cmd, rq); + } else { + rc = ub_cmd_build_block(sc, cmd, rq); + } + if (rc != 0) { + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(sc->disk->queue); + return 0; + } + + cmd->state = UB_CMDST_INIT; + cmd->done = ub_rw_cmd_done; + cmd->back = rq; + + cmd->tag = sc->tagcnt++; + if ((rc = ub_submit_scsi(sc, cmd)) != 0) { + ub_put_cmd(sc, cmd); + ub_end_rq(rq, 0); + blk_start_queue(sc->disk->queue); + return 0; + } + + return 0; +} + +static int ub_cmd_build_block(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + struct request *rq) +{ + int ub_dir; +#if 0 /* We use rq->buffer for now */ + struct scatterlist *sg; + int n_elem; +#endif + unsigned int block, nblks; + if (rq_data_dir(rq) == WRITE) ub_dir = UB_DIR_WRITE; else @@ -652,6 +690,7 @@ return 0; } #endif + /* * XXX Unfortunately, this check does not work. It is quite possible * to get bogus non-null rq->buffer if you allow sg by mistake. @@ -663,13 +702,12 @@ */ static int do_print = 1; if (do_print) { - printk(KERN_WARNING "%s: unmapped request\n", sc->name); + printk(KERN_WARNING "%s: unmapped block request" + " flags 0x%lx sectors %lu\n", + sc->name, rq->flags, rq->nr_sectors); do_print = 0; } - ub_put_cmd(sc, cmd); - ub_end_rq(rq, 0); - blk_start_queue(q); - return 0; + return -1; } /* @@ -681,7 +719,6 @@ block = rq->sector >> sc->capacity.bshift; nblks = rq->nr_sectors >> sc->capacity.bshift; - memset(cmd, 0, sizeof(struct ub_scsi_cmd)); cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */ cmd->cdb[2] = block >> 24; @@ -691,27 +728,44 @@ cmd->cdb[7] = nblks >> 8; cmd->cdb[8] = nblks; cmd->cdb_len = 10; + cmd->dir = ub_dir; - cmd->state = UB_CMDST_INIT; cmd->data = rq->buffer; cmd->len = rq->nr_sectors * 512; - cmd->done = ub_rw_cmd_done; - cmd->back = rq; - - cmd->tag = sc->tagcnt++; - if ((rc = ub_submit_scsi(sc, cmd)) != 0) { - ub_put_cmd(sc, cmd); - ub_end_rq(rq, 0); - blk_start_queue(q); - return 0; - } return 0; } -static void ub_bd_rq_fn(request_queue_t *q) +static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_scsi_cmd *cmd, + struct request *rq) { - do { } while (ub_bd_rq_fn_1(q) == 0); + + if (rq->data_len != 0 && rq->data == NULL) { + static int do_print = 1; + if (do_print) { + printk(KERN_WARNING "%s: unmapped packet request" + " flags 0x%lx length %d\n", + sc->name, rq->flags, rq->data_len); + do_print = 0; + } + return -1; + } + + memcpy(&cmd->cdb, rq->cmd, rq->cmd_len); + cmd->cdb_len = rq->cmd_len; + + if (rq->data_len == 0) { + cmd->dir = UB_DIR_NONE; + } else { + if (rq_data_dir(rq) == WRITE) + cmd->dir = UB_DIR_WRITE; + else + cmd->dir = UB_DIR_READ; + } + cmd->data = rq->data; + cmd->len = rq->data_len; + + return 0; } static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) @@ -721,6 +775,12 @@ request_queue_t *q = disk->queue; int uptodate; + if (blk_pc_request(rq)) { + /* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */ + memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE); + rq->sense_len = UB_SENSE_SIZE; + } + if (cmd->error == 0) uptodate = 1; else @@ -779,6 +839,17 @@ bcb = &sc->work_bcb; + /* + * ``If the allocation length is eighteen or greater, and a device + * server returns less than eithteen bytes of data, the application + * client should assume that the bytes not transferred would have been + * zeroes had the device server returned those bytes.'' + * + * We zero sense for all commands so that when a packet request + * fails it does not return a stale sense. + */ + memset(&sc->top_sense, 0, UB_SENSE_SIZE); + /* set up the command wrapper */ bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->Tag = cmd->tag; /* Endianness is not important */ @@ -1222,14 +1293,6 @@ goto error; } - /* - * ``If the allocation length is eighteen or greater, and a device - * server returns less than eithteen bytes of data, the application - * client should assume that the bytes not transferred would have been - * zeroes had the device server returned those bytes.'' - */ - memset(&sc->top_sense, 0, UB_SENSE_SIZE); - scmd = &sc->top_rqs_cmd; scmd->cdb[0] = REQUEST_SENSE; scmd->cdb[4] = UB_SENSE_SIZE; @@ -1495,30 +1558,10 @@ static int ub_bd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { -// void __user *usermem = (void *) arg; -// struct carm_port *port = ino->i_bdev->bd_disk->private_data; -// struct hd_geometry geom; - -#if 0 - switch (cmd) { - case HDIO_GETGEO: - if (usermem == NULL) // XXX Bizzare. Why? - return -EINVAL; - - geom.heads = (u8) port->dev_geom_head; - geom.sectors = (u8) port->dev_geom_sect; - geom.cylinders = port->dev_geom_cyl; - geom.start = get_start_sect(ino->i_bdev); - - if (copy_to_user(usermem, &geom, sizeof(geom))) - return -EFAULT; - return 0; - - default: ; - } -#endif + struct gendisk *disk = inode->i_bdev->bd_disk; + void __user *usermem = (void __user *) arg; - return -ENOTTY; + return scsi_cmd_ioctl(filp, disk, cmd, usermem); } /* diff -Nru a/drivers/usb/Kconfig b/drivers/usb/Kconfig --- a/drivers/usb/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/Kconfig 2005-02-04 22:22:37 -08:00 @@ -56,6 +56,8 @@ source "drivers/usb/net/Kconfig" +source "drivers/usb/mon/Kconfig" + comment "USB port drivers" depends on USB diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile --- a/drivers/usb/Makefile 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/Makefile 2005-02-04 22:22:37 -08:00 @@ -6,6 +6,8 @@ obj-$(CONFIG_USB) += core/ +obj-$(CONFIG_USB_MON) += mon/ + obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c --- a/drivers/usb/class/cdc-acm.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/class/cdc-acm.c 2005-02-04 22:22:37 -08:00 @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -97,9 +98,12 @@ /* devices aren't required to support these requests. * the cdc acm descriptor tells whether they do... */ -#define acm_set_control(acm, control) acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0) -#define acm_set_line(acm, line) acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line)) -#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0) +#define acm_set_control(acm, control) \ + acm_ctrl_msg(acm, USB_CDC_REQ_SET_CONTROL_LINE_STATE, control, NULL, 0) +#define acm_set_line(acm, line) \ + acm_ctrl_msg(acm, USB_CDC_REQ_SET_LINE_CODING, 0, line, sizeof *(line)) +#define acm_send_break(acm, ms) \ + acm_ctrl_msg(acm, USB_CDC_REQ_SEND_BREAK, ms, NULL, 0) /* * Interrupt handlers for various ACM device responses @@ -109,7 +113,7 @@ static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs) { struct acm *acm = urb->context; - struct usb_ctrlrequest *dr = urb->transfer_buffer; + struct usb_cdc_notification *dr = urb->transfer_buffer; unsigned char *data; int newctrl; int status; @@ -133,14 +137,14 @@ goto exit; data = (unsigned char *)(dr + 1); - switch (dr->bRequest) { + switch (dr->bNotificationType) { - case ACM_IRQ_NETWORK: + case USB_CDC_NOTIFY_NETWORK_CONNECTION: dbg("%s network", dr->wValue ? "connected to" : "disconnected from"); break; - case ACM_IRQ_LINE_STATE: + case USB_CDC_NOTIFY_SERIAL_STATE: newctrl = le16_to_cpu(get_unaligned((__le16 *) data)); @@ -160,8 +164,9 @@ break; default: - dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d", - dr->bRequest, dr->wIndex, dr->wLength, data[0], data[1]); + dbg("unknown notification %d received: index %d len %d data0 %d data1 %d", + dr->bNotificationType, dr->wIndex, + dr->wLength, data[0], data[1]); break; } exit: @@ -485,32 +490,34 @@ { struct acm *acm = tty->driver_data; struct termios *termios = tty->termios; - struct acm_line newline; + struct usb_cdc_line_coding newline; int newctrl = acm->ctrlout; if (!ACM_READY(acm)) return; - newline.speed = cpu_to_le32p(acm_tty_speed + + newline.dwDTERate = cpu_to_le32p(acm_tty_speed + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); - newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0; - newline.parity = termios->c_cflag & PARENB ? + newline.bCharFormat = termios->c_cflag & CSTOPB ? 2 : 0; + newline.bParityType = termios->c_cflag & PARENB ? (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; - newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; + newline.bDataBits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; acm->clocal = ((termios->c_cflag & CLOCAL) != 0); - if (!newline.speed) { - newline.speed = acm->line.speed; + if (!newline.dwDTERate) { + newline.dwDTERate = acm->line.dwDTERate; newctrl &= ~ACM_CTRL_DTR; } else newctrl |= ACM_CTRL_DTR; if (newctrl != acm->ctrlout) acm_set_control(acm, acm->ctrlout = newctrl); - if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) { - memcpy(&acm->line, &newline, sizeof(struct acm_line)); - dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits); + if (memcmp(&acm->line, &newline, sizeof newline)) { + memcpy(&acm->line, &newline, sizeof newline); + dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate), + newline.bCharFormat, newline.bParityType, + newline.bDataBits); acm_set_line(acm, &acm->line); } } @@ -522,7 +529,7 @@ static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id) { - struct union_desc *union_header = NULL; + struct usb_cdc_union_desc *union_header = NULL; char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -573,21 +580,22 @@ } switch (buffer [2]) { - case CDC_UNION_TYPE: /* we've found it */ + case USB_CDC_UNION_TYPE: /* we've found it */ if (union_header) { err("More than one union descriptor, skipping ..."); goto next_desc; } - union_header = (struct union_desc *)buffer; + union_header = (struct usb_cdc_union_desc *) + buffer; break; - case CDC_COUNTRY_TYPE: /* maybe somehow export */ + case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ break; /* for now we ignore it */ - case CDC_HEADER_TYPE: /* maybe check version */ + case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ - case CDC_AC_MANAGEMENT_TYPE: + case USB_CDC_ACM_TYPE: ac_management_function = buffer[3]; break; - case CDC_CALL_MANAGEMENT_TYPE: + case USB_CDC_CALL_MANAGEMENT_TYPE: call_management_function = buffer[3]; call_interface_num = buffer[4]; if ((call_management_function & 3) != 3) @@ -750,8 +758,8 @@ acm_set_control(acm, acm->ctrlout); - acm->line.speed = cpu_to_le32(9600); - acm->line.databits = 8; + acm->line.dwDTERate = cpu_to_le32(9600); + acm->line.bDataBits = 8; acm_set_line(acm, &acm->line); usb_driver_claim_interface(&acm_driver, data_interface, acm); @@ -831,14 +839,20 @@ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, /* control interfaces with various AT-command sets */ - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 1) }, - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 2) }, - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 3) }, - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 4) }, - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 5) }, - { USB_INTERFACE_INFO(USB_CLASS_COMM, 2, 6) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_V25TER) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_PCCA101) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_PCCA101_WAKE) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_GSM) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_3G ) }, + { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, + USB_CDC_ACM_PROTO_AT_CDMA) }, - /* NOTE: COMM/2/0xff is likely MSFT RNDIS ... NOT a modem!! */ + /* NOTE: COMM/ACM/0xff is likely MSFT RNDIS ... NOT a modem!! */ { } }; diff -Nru a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h --- a/drivers/usb/class/cdc-acm.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/class/cdc-acm.h 2005-02-04 22:22:37 -08:00 @@ -27,24 +27,6 @@ #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) -#define ACM_REQ_COMMAND 0x00 -#define ACM_REQ_RESPONSE 0x01 -#define ACM_REQ_SET_FEATURE 0x02 -#define ACM_REQ_GET_FEATURE 0x03 -#define ACM_REQ_CLEAR_FEATURE 0x04 - -#define ACM_REQ_SET_LINE 0x20 -#define ACM_REQ_GET_LINE 0x21 -#define ACM_REQ_SET_CONTROL 0x22 -#define ACM_REQ_SEND_BREAK 0x23 - -/* - * IRQs. - */ - -#define ACM_IRQ_NETWORK 0x00 -#define ACM_IRQ_LINE_STATE 0x20 - /* * Output control lines. */ @@ -66,17 +48,6 @@ #define ACM_CTRL_OVERRUN 0x40 /* - * Line speed and caracter encoding. - */ - -struct acm_line { - __le32 speed; - __u8 stopbits; - __u8 parity; - __u8 databits; -} __attribute__ ((packed)); - -/* * Internal driver structures. */ @@ -88,7 +59,7 @@ struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ u8 *ctrl_buffer, *read_buffer, *write_buffer; /* buffers of urbs */ dma_addr_t ctrl_dma, read_dma, write_dma; /* dma handles of buffers */ - struct acm_line line; /* line coding (bits, stop, parity) */ + struct usb_cdc_line_coding line; /* bits, stop, parity */ struct work_struct work; /* work queue entry for line discipline waking up */ struct tasklet_struct bh; /* rx processing */ spinlock_t throttle_lock; /* synchronize throtteling and read callback */ @@ -104,24 +75,6 @@ unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ }; - -/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ -struct union_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 bMasterInterface0; - u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -/* class specific descriptor types */ -#define CDC_HEADER_TYPE 0x00 -#define CDC_CALL_MANAGEMENT_TYPE 0x01 -#define CDC_AC_MANAGEMENT_TYPE 0x02 -#define CDC_UNION_TYPE 0x06 -#define CDC_COUNTRY_TYPE 0x07 #define CDC_DATA_INTERFACE_TYPE 0x0a diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/core/hcd.c 2005-02-04 22:22:37 -08:00 @@ -45,6 +45,7 @@ #include "usb.h" #include "hcd.h" +#include "hub.h" // #define USB_BANDWIDTH_MESSAGES @@ -86,6 +87,7 @@ /* host controllers we manage */ LIST_HEAD (usb_bus_list); +EXPORT_SYMBOL_GPL (usb_bus_list); /* used when allocating bus numbers */ #define USB_MAXBUS 64 @@ -96,6 +98,7 @@ /* used when updating list of hcds */ DECLARE_MUTEX (usb_bus_list_lock); /* exported only for usbfs */ +EXPORT_SYMBOL_GPL (usb_bus_list_lock); /* used when updating hcd data */ static DEFINE_SPINLOCK(hcd_data_lock); @@ -271,6 +274,10 @@ *utf++ = *s++; *utf++ = 0; } + if (utfmax > 0) { + *utf = *s; + ++retval; + } return retval; } @@ -295,30 +302,40 @@ // language ids if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes string data */ - *data++ = 0x09; *data++ = 0x04; /* MSFT-speak for "en-us" */ - return 4; + buf[0] = 4; buf[1] = 3; /* 4 bytes string data */ + buf[2] = 0x09; buf[3] = 0x04; /* MSFT-speak for "en-us" */ + len = min (len, 4); + memcpy (data, buf, len); + return len; // serial number } else if (id == 1) { - strcpy (buf, hcd->self.bus_name); + strlcpy (buf, hcd->self.bus_name, sizeof buf); // product description } else if (id == 2) { - strcpy (buf, hcd->product_desc); + strlcpy (buf, hcd->product_desc, sizeof buf); // id 3 == vendor description } else if (id == 3) { - sprintf (buf, "%s %s %s", system_utsname.sysname, + snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname, system_utsname.release, hcd->driver->description); // unsupported IDs --> "protocol stall" } else - return 0; + return -EPIPE; - data [0] = 2 * (strlen (buf) + 1); - data [1] = 3; /* type == string */ - return 2 + ascii2utf (buf, data + 2, len - 2); + switch (len) { /* All cases fall through */ + default: + len = 2 + ascii2utf (buf, data + 2, len - 2); + case 2: + data [1] = 3; /* type == string */ + case 1: + data [0] = 2 * (strlen (buf) + 1); + case 0: + ; /* Compiler wants a statement here */ + } + return len; } @@ -327,11 +344,14 @@ { struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; - const u8 *bufp = NULL; u8 *ubuf = urb->transfer_buffer; + u8 tbuf [sizeof (struct usb_hub_descriptor)]; + const u8 *bufp = tbuf; int len = 0; int patch_wakeup = 0; unsigned long flags; + int status = 0; + int n; cmd = (struct usb_ctrlrequest *) urb->setup_packet; typeReq = (cmd->bRequestType << 8) | cmd->bRequest; @@ -342,17 +362,16 @@ if (wLength > urb->transfer_buffer_length) goto error; - /* set up for success */ - urb->status = 0; - urb->actual_length = wLength; + urb->actual_length = 0; switch (typeReq) { /* DEVICE REQUESTS */ case DeviceRequest | USB_REQ_GET_STATUS: - ubuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) + tbuf [0] = (hcd->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP) | (1 << USB_DEVICE_SELF_POWERED); - ubuf [1] = 0; + tbuf [1] = 0; + len = 2; break; case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: if (wValue == USB_DEVICE_REMOTE_WAKEUP) @@ -367,7 +386,8 @@ goto error; break; case DeviceRequest | USB_REQ_GET_CONFIGURATION: - ubuf [0] = 1; + tbuf [0] = 1; + len = 1; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: break; @@ -394,16 +414,18 @@ patch_wakeup = 1; break; case USB_DT_STRING << 8: - urb->actual_length = rh_string ( - wValue & 0xff, hcd, - ubuf, wLength); + n = rh_string (wValue & 0xff, hcd, ubuf, wLength); + if (n < 0) + goto error; + urb->actual_length = n; break; default: goto error; } break; case DeviceRequest | USB_REQ_GET_INTERFACE: - ubuf [0] = 0; + tbuf [0] = 0; + len = 1; /* FALLTHROUGH */ case DeviceOutRequest | USB_REQ_SET_INTERFACE: break; @@ -419,8 +441,9 @@ case EndpointRequest | USB_REQ_GET_STATUS: // ENDPOINT_HALT flag - ubuf [0] = 0; - ubuf [1] = 0; + tbuf [0] = 0; + tbuf [1] = 0; + len = 2; /* FALLTHROUGH */ case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_SET_FEATURE: @@ -432,19 +455,30 @@ default: /* non-generic request */ if (HCD_IS_SUSPENDED (hcd->state)) - urb->status = -EAGAIN; - else - urb->status = hcd->driver->hub_control (hcd, + status = -EAGAIN; + else { + switch (typeReq) { + case GetHubStatus: + case GetPortStatus: + len = 4; + break; + case GetHubDescriptor: + len = sizeof (struct usb_hub_descriptor); + break; + } + status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, - ubuf, wLength); + tbuf, wLength); + } break; error: /* "protocol stall" on error */ - urb->status = -EPIPE; + status = -EPIPE; } - if (urb->status) { - urb->actual_length = 0; - if (urb->status != -EPIPE) { + + if (status) { + len = 0; + if (status != -EPIPE) { dev_dbg (hcd->self.controller, "CTRL: TypeReq=0x%x val=0x%x " "idx=0x%x len=%d ==> %d\n", @@ -452,7 +486,7 @@ wLength, urb->status); } } - if (bufp) { + if (len) { if (urb->transfer_buffer_length < len) len = urb->transfer_buffer_length; urb->actual_length = len; @@ -460,13 +494,19 @@ memcpy (ubuf, bufp, len); /* report whether RH hardware supports remote wakeup */ - if (patch_wakeup) + if (patch_wakeup && + len > offsetof (struct usb_config_descriptor, + bmAttributes)) ((struct usb_config_descriptor *)ubuf)->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } /* any errors get returned through the urb completion */ local_irq_save (flags); + spin_lock (&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock (&urb->lock); usb_hcd_giveback_urb (hcd, urb, NULL); local_irq_restore (flags); return 0; @@ -746,6 +786,7 @@ up (&usb_bus_list_lock); usbfs_add_bus (bus); + usbmon_notify_bus_add (bus); dev_info (bus->controller, "new USB bus registered, assigned bus number %d\n", bus->busnum); return 0; @@ -773,6 +814,7 @@ list_del (&bus->bus_list); up (&usb_bus_list_lock); + usbmon_notify_bus_remove (bus); usbfs_remove_bus (bus); clear_bit (bus->busnum, busmap.busmap); @@ -1058,9 +1100,7 @@ * as simple as possible. */ - // NOTE: a generic device/urb monitoring hook would go here. - // hcd_monitor_hook(MONITOR_URB_SUBMIT, urb) - // It would catch submission paths for all urbs. + usbmon_urb_submit(&hcd->self, urb); /* * Atomically queue the urb, first to our records, then to the HCD. @@ -1087,6 +1127,7 @@ spin_unlock_irqrestore (&hcd_data_lock, flags); if (status) { INIT_LIST_HEAD (&urb->urb_list); + usbmon_urb_submit_error(&hcd->self, urb, status); return status; } @@ -1103,8 +1144,6 @@ * valid and usb_buffer_{sync,unmap}() not be needed, since * they could clobber root hub response data. */ - urb->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP - | URB_NO_SETUP_DMA_MAP); status = rh_urb_enqueue (hcd, urb); goto done; } @@ -1139,6 +1178,7 @@ if (urb->reject) wake_up (&usb_kill_urb_queue); usb_put_urb (urb); + usbmon_urb_submit_error(&hcd->self, urb, status); } return status; } @@ -1461,14 +1501,13 @@ */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) { - urb_unlink (urb); + int at_root_hub; - // NOTE: a generic device/urb monitoring hook would go here. - // hcd_monitor_hook(MONITOR_URB_FINISH, urb, dev) - // It would catch exit/unlink paths for all urbs. + at_root_hub = (urb->dev == hcd->self.root_hub); + urb_unlink (urb); /* lower level hcd code should use *_dma exclusively */ - if (hcd->self.controller->dma_mask) { + if (hcd->self.controller->dma_mask && !at_root_hub) { if (usb_pipecontrol (urb->pipe) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) dma_unmap_single (hcd->self.controller, urb->setup_dma, @@ -1484,6 +1523,7 @@ : DMA_TO_DEVICE); } + usbmon_urb_complete (&hcd->self, urb); /* pass ownership to the completion handler */ urb->complete (urb, regs); atomic_dec (&urb->use_count); @@ -1591,3 +1631,43 @@ usb_bus_put(&hcd->self); } EXPORT_SYMBOL (usb_put_hcd); + +/*-------------------------------------------------------------------------*/ + +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + +struct usb_mon_operations *mon_ops; + +/* + * The registration is unlocked. + * We do it this way because we do not want to lock in hot paths. + * + * Notice that the code is minimally error-proof. Because usbmon needs + * symbols from usbcore, usbcore gets referenced and cannot be unloaded first. + */ + +int usb_mon_register (struct usb_mon_operations *ops) +{ + + if (mon_ops) + return -EBUSY; + + mon_ops = ops; + mb(); + return 0; +} +EXPORT_SYMBOL_GPL (usb_mon_register); + +void usb_mon_deregister (void) +{ + + if (mon_ops == NULL) { + printk(KERN_ERR "USB: monitor was not registered\n"); + return; + } + mon_ops = NULL; + mb(); +} +EXPORT_SYMBOL_GPL (usb_mon_deregister); + +#endif /* CONFIG_USB_MON */ diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/core/hcd.h 2005-02-04 22:22:37 -08:00 @@ -411,6 +411,66 @@ /*-------------------------------------------------------------------------*/ +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + +struct usb_mon_operations { + void (*urb_submit)(struct usb_bus *bus, struct urb *urb); + void (*urb_submit_error)(struct usb_bus *bus, struct urb *urb, int err); + void (*urb_complete)(struct usb_bus *bus, struct urb *urb); + /* void (*urb_unlink)(struct usb_bus *bus, struct urb *urb); */ + void (*bus_add)(struct usb_bus *bus); + void (*bus_remove)(struct usb_bus *bus); +}; + +extern struct usb_mon_operations *mon_ops; + +static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) +{ + if (bus->monitored) + (*mon_ops->urb_submit)(bus, urb); +} + +static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, + int error) +{ + if (bus->monitored) + (*mon_ops->urb_submit_error)(bus, urb, error); +} + +static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) +{ + if (bus->monitored) + (*mon_ops->urb_complete)(bus, urb); +} + +static inline void usbmon_notify_bus_add(struct usb_bus *bus) +{ + if (mon_ops) + (*mon_ops->bus_add)(bus); +} + +static inline void usbmon_notify_bus_remove(struct usb_bus *bus) +{ + if (mon_ops) + (*mon_ops->bus_remove)(bus); +} + +int usb_mon_register(struct usb_mon_operations *ops); +void usb_mon_deregister(void); + +#else + +static inline void usbmon_urb_submit(struct usb_bus *bus, struct urb *urb) {} +static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb, + int error) {} +static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb) {} +static inline void usbmon_notify_bus_add(struct usb_bus *bus) {} +static inline void usbmon_notify_bus_remove(struct usb_bus *bus) {} + +#endif /* CONFIG_USB_MON */ + +/*-------------------------------------------------------------------------*/ + /* hub.h ... DeviceRemovable in 2.4.2-ac11, gone in 2.4.10 */ // bleech -- resurfaced in 2.4.11 or 2.4.12 #define bitmap DeviceRemovable diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c --- a/drivers/usb/core/hub.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/core/hub.c 2005-02-04 22:22:37 -08:00 @@ -74,7 +74,7 @@ MODULE_PARM_DESC(old_scheme_first, "start with the old device initialization scheme"); -static int use_both_schemes = 0; +static int use_both_schemes = 1; module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(use_both_schemes, "try the other device initialization scheme if the " @@ -459,6 +459,7 @@ int status; hub->quiescing = 0; + hub->activating = 1; status = usb_submit_urb(hub->urb, GFP_NOIO); if (status < 0) dev_err(hub->intfdev, "activate --> %d\n", status); @@ -466,7 +467,6 @@ schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD); /* scan all ports ASAP */ - hub->event_bits[0] = (1UL << (hub->descriptor->bNbrPorts + 1)) - 1; kick_khubd(hub); } @@ -689,7 +689,6 @@ hub->indicator [0] = INDICATOR_CYCLE; hub_power_on(hub); - hub->change_bits[0] = (1UL << (hub->descriptor->bNbrPorts + 1)) - 2; hub_activate(hub); return 0; @@ -1235,10 +1234,10 @@ */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { static int __usb_suspend_device (struct usb_device *, - int port1, u32 state); + int port1, pm_message_t state); err = __usb_suspend_device(udev, udev->bus->otg_port, - PM_SUSPEND_MEM); + PMSG_SUSPEND); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } @@ -1523,7 +1522,7 @@ * Linux (2.6) currently has NO mechanisms to initiate that: no khubd * timer, no SRP, no requests through sysfs. */ -int __usb_suspend_device (struct usb_device *udev, int port1, u32 state) +int __usb_suspend_device (struct usb_device *udev, int port1, pm_message_t state) { int status; @@ -1621,7 +1620,7 @@ /** * usb_suspend_device - suspend a usb device * @udev: device that's no longer in active use - * @state: PM_SUSPEND_MEM to suspend + * @state: PMSG_SUSPEND to suspend * Context: must be able to sleep; device not locked * * Suspends a USB device that isn't in active use, conserving power. @@ -1670,7 +1669,7 @@ usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); - udev->dev.power.power_state = PM_SUSPEND_ON; + udev->dev.power.power_state = PMSG_ON; /* 10.5.4.5 says be sure devices in the tree are still there. * For now let's assume the device didn't go crazy on resume, @@ -1871,7 +1870,7 @@ return status; } -static int hub_suspend(struct usb_interface *intf, u32 state) +static int hub_suspend(struct usb_interface *intf, pm_message_t state) { struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; @@ -1943,7 +1942,7 @@ } up(&udev->serialize); } - intf->dev.power.power_state = PM_SUSPEND_ON; + intf->dev.power.power_state = PMSG_ON; hub_activate(hub); return 0; @@ -2178,24 +2177,35 @@ retval = -ENOMEM; continue; } - buf->bMaxPacketSize0 = 0; /* Use a short timeout the first time through, * so that recalcitrant full-speed devices with * 8- or 16-byte ep0-maxpackets won't slow things * down tremendously by NAKing the unexpectedly - * early status stage. Also, retry on length 0 - * or stall; some devices are flakey. + * early status stage. Also, retry on all errors; + * some devices are flakey. */ for (j = 0; j < 3; ++j) { + buf->bMaxPacketSize0 = 0; r = usb_control_msg(udev, usb_rcvaddr0pipe(), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, (i ? HZ * USB_CTRL_GET_TIMEOUT : HZ)); - if (r == 0 || r == -EPIPE) - continue; - if (r < 0) + switch (buf->bMaxPacketSize0) { + case 8: case 16: case 32: case 64: + if (buf->bDescriptorType == + USB_DT_DEVICE) { + r = 0; + break; + } + /* FALL THROUGH */ + default: + if (r == 0) + r = -EPROTO; + break; + } + if (r == 0) break; } udev->descriptor.bMaxPacketSize0 = @@ -2211,10 +2221,7 @@ retval = -ENODEV; goto fail; } - switch (udev->descriptor.bMaxPacketSize0) { - case 64: case 32: case 16: case 8: - break; - default: + if (r) { dev_err(&udev->dev, "device descriptor " "read/%s, error %d\n", "64", r); @@ -2627,7 +2634,7 @@ for (i = 1; i <= hub->descriptor->bNbrPorts; i++) { connect_change = test_bit(i, hub->change_bits); if (!test_and_clear_bit(i, hub->event_bits) && - !connect_change) + !connect_change && !hub->activating) continue; ret = hub_port_status(hub, i, @@ -2635,6 +2642,11 @@ if (ret < 0) continue; + if (hub->activating && !hdev->children[i-1] && + (portstatus & + USB_PORT_STAT_CONNECTION)) + connect_change = 1; + if (portchange & USB_PORT_STAT_C_CONNECTION) { clear_port_feature(hdev, i, USB_PORT_FEAT_C_CONNECTION); @@ -2724,6 +2736,8 @@ hub_power_on(hub); } } + + hub->activating = 0; loop: usb_unlock_device(hdev); diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/core/hub.h 2005-02-04 22:22:37 -08:00 @@ -215,6 +215,7 @@ u8 power_budget; /* in 2mA units; or zero */ unsigned quiescing:1; + unsigned activating:1; unsigned has_indicators:1; enum hub_led_mode indicator[USB_MAXCHILDREN]; diff -Nru a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig --- a/drivers/usb/gadget/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/Kconfig 2005-02-04 22:22:37 -08:00 @@ -87,10 +87,10 @@ default USB_GADGET config USB_GADGET_PXA2XX - boolean "PXA 2xx or IXP 4xx" - depends on ARCH_PXA || ARCH_IXP4XX + boolean "PXA 25x or IXP 4xx" + depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX help - Intel's PXA 2xx series XScale ARM-5TE processors include + Intel's PXA 25x series XScale ARM-5TE processors include an integrated full speed USB 1.1 device controller. The controller in the IXP 4xx series is register-compatible. @@ -194,7 +194,7 @@ config USB_GADGET_OMAP boolean "OMAP USB Device Controller" depends on ARCH_OMAP - select ISP1301_OMAP if MACH_OMAP_H2 + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 diff -Nru a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c --- a/drivers/usb/gadget/ether.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/ether.c 2005-02-04 22:22:37 -08:00 @@ -49,6 +49,7 @@ #include #include +#include #include #include @@ -432,8 +433,8 @@ /* status endpoint is optional; this may be patched later */ .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6, /* ethernet control model */ - .bInterfaceProtocol = 0, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, .iInterface = STRING_CONTROL, }; #endif @@ -447,46 +448,26 @@ .bInterfaceNumber = 0, .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 2, /* abstract control model */ - .bInterfaceProtocol = 0xff, /* vendor specific */ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, .iInterface = STRING_RNDIS_CONTROL, }; #endif #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ -struct header_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u16 bcdCDC; -} __attribute__ ((packed)); - -static const struct header_desc header_desc = { +static const struct usb_cdc_header_desc header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = 0, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, .bcdCDC = __constant_cpu_to_le16 (0x0110), }; -/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ -struct union_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 bMasterInterface0; - u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -static const struct union_desc union_desc = { +static const struct usb_cdc_union_desc union_desc = { .bLength = sizeof union_desc, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = 6, + .bDescriptorSubType = USB_CDC_UNION_TYPE, .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of DATA interface */ @@ -496,64 +477,31 @@ #ifdef CONFIG_USB_ETH_RNDIS -/* "Call Management Descriptor" from CDC spec 5.2.3.3 */ -struct call_mgmt_descriptor { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 bmCapabilities; - u8 bDataInterface; -} __attribute__ ((packed)); - -static const struct call_mgmt_descriptor call_mgmt_descriptor = { +static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { .bLength = sizeof call_mgmt_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = 0x01, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, .bmCapabilities = 0x00, .bDataInterface = 0x01, }; - -/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.4 */ -struct acm_descriptor { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 bmCapabilities; -} __attribute__ ((packed)); - -static struct acm_descriptor acm_descriptor = { +static struct usb_cdc_acm_descriptor acm_descriptor = { .bLength = sizeof acm_descriptor, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = 0x02, + .bDescriptorSubType = USB_CDC_ACM_TYPE, - .bmCapabilities = 0X00, + .bmCapabilities = 0x00, }; #endif #ifdef DEV_CONFIG_CDC -/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ -struct ether_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 iMACAddress; - u32 bmEthernetStatistics; - u16 wMaxSegmentSize; - u16 wNumberMCFilters; - u8 bNumberPowerFilters; -} __attribute__ ((packed)); - -static const struct ether_desc ether_desc = { +static const struct usb_cdc_ether_desc ether_desc = { .bLength = sizeof ether_desc, .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = 0x0f, + .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, /* this descriptor actually adds value, surprise! */ .iMACAddress = STRING_ETHADDR, @@ -1242,47 +1190,30 @@ /*-------------------------------------------------------------------------*/ -/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications - * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS - * and RNDIS also defines its own bit-incompatible notifications - */ -#define CDC_NOTIFY_NETWORK_CONNECTION 0x00 /* required; 6.3.1 */ -#define CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 /* optional; 6.3.2 */ -#define CDC_NOTIFY_SPEED_CHANGE 0x2a /* required; 6.3.8 */ - #ifdef DEV_CONFIG_CDC -struct cdc_notification { - u8 bmRequestType; - u8 bNotificationType; - u16 wValue; - u16 wIndex; - u16 wLength; - - /* SPEED_CHANGE data looks like this */ - u32 data [2]; -}; - static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) { - struct cdc_notification *event = req->buf; - int value = req->status; - struct eth_dev *dev = ep->driver_data; + struct usb_cdc_notification *event = req->buf; + int value = req->status; + struct eth_dev *dev = ep->driver_data; /* issue the second notification if host reads the first */ - if (event->bNotificationType == CDC_NOTIFY_NETWORK_CONNECTION + if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION && value == 0) { + __le32 *data = req->buf + sizeof *event; + event->bmRequestType = 0xA1; - event->bNotificationType = CDC_NOTIFY_SPEED_CHANGE; + event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; event->wValue = __constant_cpu_to_le16 (0); event->wIndex = __constant_cpu_to_le16 (1); event->wLength = __constant_cpu_to_le16 (8); /* SPEED_CHANGE data is up/down speeds in bits/sec */ - event->data [0] = event->data [1] = + data [0] = data [1] = cpu_to_le32( (dev->gadget->speed == USB_SPEED_HIGH) ? (13 * 512 * 8 * 1000 * 8) - : (19 * 64 * 1 * 1000 * 8); + : (19 * 64 * 1 * 1000 * 8)); req->length = 16; value = usb_ep_queue (ep, req, GFP_ATOMIC); @@ -1300,9 +1231,9 @@ static void issue_start_status (struct eth_dev *dev) { - struct usb_request *req; - struct cdc_notification *event; - int value; + struct usb_request *req; + struct usb_cdc_notification *event; + int value; DEBUG (dev, "%s, flush old status first\n", __FUNCTION__); @@ -1336,7 +1267,7 @@ */ event = req->buf; event->bmRequestType = 0xA1; - event->bNotificationType = CDC_NOTIFY_NETWORK_CONNECTION; + event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; event->wValue = __constant_cpu_to_le16 (1); /* connected */ event->wIndex = __constant_cpu_to_le16 (1); event->wLength = 0; @@ -1364,26 +1295,6 @@ req->status, req->actual, req->length); } -/* see section 3.8.2 table 10 of the CDC spec for more ethernet - * requests, mostly for filters (multicast, pm) and statistics - * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the - * encapsulated command mechanism. - */ -#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 /* optional */ -#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */ -#define CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 /* optional */ -#define CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 /* optional */ -#define CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 /* optional */ -#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */ -#define CDC_GET_ETHERNET_STATISTIC 0x44 /* optional */ - -/* table 62; bits in cdc_filter */ -#define CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) -#define CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ -#define CDC_PACKET_TYPE_DIRECTED (1 << 2) -#define CDC_PACKET_TYPE_BROADCAST (1 << 3) -#define CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ - #ifdef CONFIG_USB_ETH_RNDIS static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) @@ -1393,7 +1304,7 @@ "rndis response complete --> %d, %d/%d\n", req->status, req->actual, req->length); - /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ + /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ } static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) @@ -1401,7 +1312,7 @@ struct eth_dev *dev = ep->driver_data; int status; - /* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */ + /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ spin_lock(&dev->lock); status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); if (status < 0) @@ -1426,6 +1337,9 @@ struct eth_dev *dev = get_gadget_data (gadget); struct usb_request *req = dev->req; int value = -EOPNOTSUPP; + u16 wIndex = ctrl->wIndex; + u16 wValue = ctrl->wValue; + u16 wLength = ctrl->wLength; /* descriptors just go into the pre-allocated ep0 buffer, * while config change events may enable network traffic. @@ -1436,17 +1350,17 @@ case USB_REQ_GET_DESCRIPTOR: if (ctrl->bRequestType != USB_DIR_IN) break; - switch (ctrl->wValue >> 8) { + switch (wValue >> 8) { case USB_DT_DEVICE: - value = min (ctrl->wLength, (u16) sizeof device_desc); + value = min (wLength, (u16) sizeof device_desc); memcpy (req->buf, &device_desc, value); break; #ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: if (!gadget->is_dualspeed) break; - value = min (ctrl->wLength, (u16) sizeof dev_qualifier); + value = min (wLength, (u16) sizeof dev_qualifier); memcpy (req->buf, &dev_qualifier, value); break; @@ -1457,18 +1371,18 @@ #endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: value = config_buf (gadget->speed, req->buf, - ctrl->wValue >> 8, - ctrl->wValue & 0xff, + wValue >> 8, + wValue & 0xff, gadget->is_otg); if (value >= 0) - value = min (ctrl->wLength, (u16) value); + value = min (wLength, (u16) value); break; case USB_DT_STRING: value = usb_gadget_get_string (&stringtab, - ctrl->wValue & 0xff, req->buf); + wValue & 0xff, req->buf); if (value >= 0) - value = min (ctrl->wLength, (u16) value); + value = min (wLength, (u16) value); break; } break; @@ -1481,22 +1395,22 @@ else if (gadget->a_alt_hnp_support) DEBUG (dev, "HNP needs a different root port\n"); spin_lock (&dev->lock); - value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC); + value = eth_set_config (dev, wValue, GFP_ATOMIC); spin_unlock (&dev->lock); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) break; *(u8 *)req->buf = dev->config; - value = min (ctrl->wLength, (u16) 1); + value = min (wLength, (u16) 1); break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE || !dev->config - || ctrl->wIndex > 1) + || wIndex > 1) break; - if (!dev->cdc && ctrl->wIndex != 0) + if (!dev->cdc && wIndex != 0) break; spin_lock (&dev->lock); @@ -1510,9 +1424,9 @@ } #ifdef DEV_CONFIG_CDC - switch (ctrl->wIndex) { + switch (wIndex) { case 0: /* control/master intf */ - if (ctrl->wValue != 0) + if (wValue != 0) break; if (dev->status_ep) { usb_ep_disable (dev->status_ep); @@ -1521,7 +1435,7 @@ value = 0; break; case 1: /* data intf */ - if (ctrl->wValue > 1) + if (wValue > 1) break; usb_ep_disable (dev->in_ep); usb_ep_disable (dev->out_ep); @@ -1530,7 +1444,7 @@ * the default interface setting ... also, setting * the non-default interface clears filters etc. */ - if (ctrl->wValue == 1) { + if (wValue == 1) { usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); netif_carrier_on (dev->net); @@ -1561,36 +1475,36 @@ case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) || !dev->config - || ctrl->wIndex > 1) + || wIndex > 1) break; - if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0) + if (!(dev->cdc || dev->rndis) && wIndex != 0) break; /* for CDC, iff carrier is on, data interface is active. */ - if (dev->rndis || ctrl->wIndex != 1) + if (dev->rndis || wIndex != 1) *(u8 *)req->buf = 0; else *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; - value = min (ctrl->wLength, (u16) 1); + value = min (wLength, (u16) 1); break; #ifdef DEV_CONFIG_CDC - case CDC_SET_ETHERNET_PACKET_FILTER: + case USB_CDC_SET_ETHERNET_PACKET_FILTER: /* see 6.2.30: no data, wIndex = interface, * wValue = packet filter bitmap */ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) || !dev->cdc || dev->rndis - || ctrl->wLength != 0 - || ctrl->wIndex > 1) + || wLength != 0 + || wIndex > 1) break; - DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue); + DEBUG (dev, "NOP packet filter %04x\n", wValue); /* NOTE: table 62 has 5 filter bits to reduce traffic, * and we "must" support multicast and promiscuous. * this NOP implements a bad filter (always promisc) */ - dev->cdc_filter = ctrl->wValue; + dev->cdc_filter = wValue; value = 0; break; #endif /* DEV_CONFIG_CDC */ @@ -1599,28 +1513,28 @@ /* RNDIS uses the CDC command encapsulation mechanism to implement * an RPC scheme, with much getting/setting of attributes by OID. */ - case CDC_SEND_ENCAPSULATED_COMMAND: + case USB_CDC_SEND_ENCAPSULATED_COMMAND: if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) || !dev->rndis - || ctrl->wLength > USB_BUFSIZ - || ctrl->wValue + || wLength > USB_BUFSIZ + || wValue || rndis_control_intf.bInterfaceNumber - != ctrl->wIndex) + != wIndex) break; /* read the request, then process it */ - value = ctrl->wLength; + value = wLength; req->complete = rndis_command_complete; /* later, rndis_control_ack () sends a notification */ break; - case CDC_GET_ENCAPSULATED_RESPONSE: + case USB_CDC_GET_ENCAPSULATED_RESPONSE: if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) == ctrl->bRequestType && dev->rndis - // && ctrl->wLength >= 0x0400 - && !ctrl->wValue + // && wLength >= 0x0400 + && !wValue && rndis_control_intf.bInterfaceNumber - == ctrl->wIndex) { + == wIndex) { u8 *buf; /* return the result */ @@ -1640,13 +1554,13 @@ VDEBUG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, - ctrl->wValue, ctrl->wIndex, ctrl->wLength); + wValue, wIndex, wLength); } /* respond with data transfer before status phase? */ if (value >= 0) { req->length = value; - req->zero = value < ctrl->wLength + req->zero = value < wLength && (value % gadget->ep0->maxpacket) == 0; value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); if (value < 0) { @@ -1990,7 +1904,7 @@ unsigned long flags; /* FIXME check dev->cdc_filter to decide whether to send this, - * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were + * instead of acting as if USB_CDC_PACKET_TYPE_PROMISCUOUS were * always set. RNDIS has the same kind of outgoing filter. */ @@ -2124,13 +2038,13 @@ } /* Send RNDIS RESPONSE_AVAILABLE notification; - * CDC_NOTIFY_RESPONSE_AVAILABLE should work too + * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too */ resp->length = 8; resp->complete = rndis_control_ack_complete; - *((u32 *) resp->buf) = __constant_cpu_to_le32 (1); - *((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); + *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); + *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); if (length < 0) { @@ -2414,9 +2328,12 @@ "can't run RNDIS on %s\n", gadget->name); return -ENODEV; +#ifdef DEV_CONFIG_CDC + /* pxa25x only does CDC subset; often used with RNDIS */ } else if (cdc) { control_intf.bNumEndpoints = 0; /* FIXME remove endpoint from descriptor list */ +#endif } } #endif diff -Nru a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c --- a/drivers/usb/gadget/net2280.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/net2280.c 2005-02-04 22:22:37 -08:00 @@ -1,8 +1,9 @@ /* - * Driver for the NetChip 2280 USB device controller. - * Specs and errata are available from . + * Driver for the PLX NET2280 USB device controller. + * Specs and errata are available from . * - * NetChip Technology Inc. supported the development of this driver. + * PLX Technology Inc. (formerly NetChip Technology) supported the + * development of this driver. * * * CODE STATUS HIGHLIGHTS @@ -23,7 +24,7 @@ /* * Copyright (C) 2003 David Brownell - * Copyright (C) 2003 NetChip Technologies + * Copyright (C) 2003-2005 PLX Technology, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -69,8 +70,8 @@ #include -#define DRIVER_DESC "NetChip 2280 USB Peripheral Controller" -#define DRIVER_VERSION "2004 Jan 14" +#define DRIVER_DESC "PLX NET2280 USB Peripheral Controller" +#define DRIVER_VERSION "2005 Feb 03" #define DMA_ADDR_INVALID (~(dma_addr_t)0) #define EP_DONTUSE 13 /* nonzero */ @@ -113,6 +114,16 @@ /* "modprobe net2280 fifo_mode=1" etc */ module_param (fifo_mode, ushort, 0644); +/* enable_suspend -- When enabled, the driver will respond to + * USB suspend requests by powering down the NET2280. Otherwise, + * USB suspend requests will be ignored. This is acceptible for + * self-powered devices, and helps avoid some quirks. + */ +static int enable_suspend = 0; + +/* "modprobe net2280 enable_suspend=1" etc */ +module_param (enable_suspend, bool, S_IRUGO); + #define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") @@ -2561,6 +2572,8 @@ if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { if (dev->driver->suspend) dev->driver->suspend (&dev->gadget); + if (!enable_suspend) + stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); } else { if (dev->driver->resume) dev->driver->resume (&dev->gadget); diff -Nru a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c --- a/drivers/usb/gadget/omap_udc.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/omap_udc.c 2005-02-04 22:22:37 -08:00 @@ -2,7 +2,7 @@ * omap_udc.c -- for OMAP full speed udc; most chips support OTG. * * Copyright (C) 2004 Texas Instruments, Inc. - * Copyright (C) 2004 David Brownell + * Copyright (C) 2004-2005 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -2046,7 +2046,10 @@ pullup_disable (udc); } - if (machine_is_omap_innovator()) + /* boards that don't have VBUS sensing can't autogate 48MHz; + * can't enter deep sleep while a gadget driver is active. + */ + if (machine_is_omap_innovator() || machine_is_omap_osk()) omap_vbus_session(&udc->gadget, 1); done: @@ -2064,7 +2067,7 @@ if (!driver || driver != udc->driver) return -EINVAL; - if (machine_is_omap_innovator()) + if (machine_is_omap_innovator() || machine_is_omap_osk()) omap_vbus_session(&udc->gadget, 0); if (udc->transceiver) @@ -2157,13 +2160,13 @@ } } -static char *trx_mode(unsigned m) +static char *trx_mode(unsigned m, int enabled) { switch (m) { - case 3: - case 0: return "6wire"; + case 0: return enabled ? "*6wire" : "unused"; case 1: return "4wire"; case 2: return "3wire"; + case 3: return "6wire"; default: return "unknown"; } } @@ -2171,17 +2174,20 @@ static int proc_otg_show(struct seq_file *s) { u32 tmp; + u32 trans; tmp = OTG_REV_REG; - seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %08x\n", - tmp >> 4, tmp & 0xf, - USB_TRANSCEIVER_CTRL_REG); + trans = USB_TRANSCEIVER_CTRL_REG; + seq_printf(s, "OTG rev %d.%d, transceiver_ctrl %03x\n", + tmp >> 4, tmp & 0xf, trans); tmp = OTG_SYSCON_1_REG; seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s," FOURBITS "\n", tmp, - trx_mode(USB2_TRX_MODE(tmp)), - trx_mode(USB1_TRX_MODE(tmp)), - trx_mode(USB0_TRX_MODE(tmp)), + trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R), + trx_mode(USB1_TRX_MODE(tmp), trans & CONF_USB1_UNI_R), + (USB0_TRX_MODE(tmp) == 0) + ? "internal" + : trx_mode(USB0_TRX_MODE(tmp), 1), (tmp & OTG_IDLE_EN) ? " !otg" : "", (tmp & HST_IDLE_EN) ? " !host" : "", (tmp & DEV_IDLE_EN) ? " !dev" : "", diff -Nru a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c --- a/drivers/usb/gadget/rndis.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/rndis.c 2005-02-04 22:22:37 -08:00 @@ -730,7 +730,7 @@ /* FIXME use these NDIS_PACKET_TYPE_* bitflags to * filter packets in hard_start_xmit() - * NDIS_PACKET_TYPE_x == CDC_PACKET_TYPE_x for x in: + * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: * PROMISCUOUS, DIRECTED, * MULTICAST, ALL_MULTICAST, BROADCAST */ diff -Nru a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c --- a/drivers/usb/gadget/serial.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/gadget/serial.c 2005-02-04 22:22:37 -08:00 @@ -45,6 +45,7 @@ #include #include +#include #include #include "gadget_chips.h" @@ -122,80 +123,6 @@ }) -/* CDC-ACM Defines and Structures */ - -#define USB_CDC_SUBCLASS_ACM 2 - -#define USB_CDC_CTRL_PROTO_NONE 0 -#define USB_CDC_CTRL_PROTO_AT 1 -#define USB_CDC_CTRL_PROTO_VENDOR 0xff - -#define USB_CDC_SUBTYPE_HEADER 0 -#define USB_CDC_SUBTYPE_CALL_MGMT 1 -#define USB_CDC_SUBTYPE_ACM 2 -#define USB_CDC_SUBTYPE_UNION 6 - -#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 -#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 - -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 - -#define USB_CDC_1_STOP_BITS 0 -#define USB_CDC_1_5_STOP_BITS 1 -#define USB_CDC_2_STOP_BITS 2 - -#define USB_CDC_NO_PARITY 0 -#define USB_CDC_ODD_PARITY 1 -#define USB_CDC_EVEN_PARITY 2 -#define USB_CDC_MARK_PARITY 3 -#define USB_CDC_SPACE_PARITY 4 - -/* Header Functional Descriptor from CDC spec 5.2.3.1 */ -struct usb_cdc_header_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u16 bcdCDC; -} __attribute__ ((packed)); - -/* Call Management Descriptor from CDC spec 5.2.3.3 */ -struct usb_cdc_call_mgmt_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bmCapabilities; - u8 bDataInterface; -} __attribute__ ((packed)); - -/* Abstract Control Management Descriptor from CDC spec 5.2.3.4 */ -struct usb_cdc_acm_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bmCapabilities; -} __attribute__ ((packed)); - -/* Union Functional Descriptor from CDC spec 5.2.3.8 */ -struct usb_cdc_union_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - u8 bMasterInterface0; - u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -/* Line Coding Structure from CDC spec 6.2.13 */ -struct usb_cdc_line_coding { - u32 dwDTERate; - u8 bCharFormat; - u8 bParityType; - u8 bDataBits; -} __attribute__ ((packed)); - - /* Defines */ #define GS_VERSION_STR "v2.0" @@ -542,7 +469,7 @@ .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_CTRL_PROTO_AT, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, .iInterface = GS_CONTROL_STR_ID, }; @@ -560,29 +487,29 @@ static const struct usb_cdc_header_desc gs_header_desc = { .bLength = sizeof(gs_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_HEADER, + .bDescriptorSubType = USB_CDC_HEADER_TYPE, .bcdCDC = __constant_cpu_to_le16(0x0110), }; -static const struct usb_cdc_call_mgmt_desc gs_call_mgmt_descriptor = { +static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = { .bLength = sizeof(gs_call_mgmt_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_CALL_MGMT, + .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, .bmCapabilities = 0, .bDataInterface = 1, /* index of data interface */ }; -static struct usb_cdc_acm_desc gs_acm_descriptor = { +static struct usb_cdc_acm_descriptor gs_acm_descriptor = { .bLength = sizeof(gs_acm_descriptor), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_ACM, + .bDescriptorSubType = USB_CDC_ACM_TYPE, .bmCapabilities = 0, }; static const struct usb_cdc_union_desc gs_union_desc = { .bLength = sizeof(gs_union_desc), .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubType = USB_CDC_SUBTYPE_UNION, + .bDescriptorSubType = USB_CDC_UNION_TYPE, .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of data interface */ }; @@ -1674,6 +1601,9 @@ int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; + u16 wIndex = ctrl->wIndex; + u16 wValue = ctrl->wValue; + u16 wLength = ctrl->wLength; switch (ctrl->bRequestType & USB_TYPE_MASK) { case USB_TYPE_STANDARD: @@ -1686,15 +1616,15 @@ default: printk(KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, - ctrl->wIndex, ctrl->wLength); + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); break; } /* respond with data transfer before status phase? */ if (ret >= 0) { req->length = ret; - req->zero = ret < ctrl->wLength + req->zero = ret < wLength && (ret % gadget->ep0->maxpacket) == 0; ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); if (ret < 0) { @@ -1715,15 +1645,18 @@ int ret = -EOPNOTSUPP; struct gs_dev *dev = get_gadget_data(gadget); struct usb_request *req = dev->dev_ctrl_req; + u16 wIndex = ctrl->wIndex; + u16 wValue = ctrl->wValue; + u16 wLength = ctrl->wLength; switch (ctrl->bRequest) { case USB_REQ_GET_DESCRIPTOR: if (ctrl->bRequestType != USB_DIR_IN) break; - switch (ctrl->wValue >> 8) { + switch (wValue >> 8) { case USB_DT_DEVICE: - ret = min(ctrl->wLength, + ret = min(wLength, (u16)sizeof(struct usb_device_descriptor)); memcpy(req->buf, &gs_device_desc, ret); break; @@ -1732,7 +1665,7 @@ case USB_DT_DEVICE_QUALIFIER: if (!gadget->is_dualspeed) break; - ret = min(ctrl->wLength, + ret = min(wLength, (u16)sizeof(struct usb_qualifier_descriptor)); memcpy(req->buf, &gs_qualifier_desc, ret); break; @@ -1744,18 +1677,18 @@ #endif /* CONFIG_USB_GADGET_DUALSPEED */ case USB_DT_CONFIG: ret = gs_build_config_buf(req->buf, gadget->speed, - ctrl->wValue >> 8, ctrl->wValue & 0xff, + wValue >> 8, wValue & 0xff, gadget->is_otg); if (ret >= 0) - ret = min(ctrl->wLength, (u16)ret); + ret = min(wLength, (u16)ret); break; case USB_DT_STRING: /* wIndex == language code. */ ret = usb_gadget_get_string(&gs_string_table, - ctrl->wValue & 0xff, req->buf); + wValue & 0xff, req->buf); if (ret >= 0) - ret = min(ctrl->wLength, (u16)ret); + ret = min(wLength, (u16)ret); break; } break; @@ -1764,7 +1697,7 @@ if (ctrl->bRequestType != 0) break; spin_lock(&dev->dev_lock); - ret = gs_set_config(dev, ctrl->wValue); + ret = gs_set_config(dev, wValue); spin_unlock(&dev->dev_lock); break; @@ -1772,18 +1705,19 @@ if (ctrl->bRequestType != USB_DIR_IN) break; *(u8 *)req->buf = dev->dev_config; - ret = min(ctrl->wLength, (u16)1); + ret = min(wLength, (u16)1); break; case USB_REQ_SET_INTERFACE: if (ctrl->bRequestType != USB_RECIP_INTERFACE - || !dev->dev_config || ctrl->wIndex >= GS_MAX_NUM_INTERFACES) + || !dev->dev_config + || wIndex >= GS_MAX_NUM_INTERFACES) break; if (dev->dev_config == GS_BULK_CONFIG_ID - && ctrl->wIndex != GS_BULK_INTERFACE_ID) + && wIndex != GS_BULK_INTERFACE_ID) break; /* no alternate interface settings */ - if (ctrl->wValue != 0) + if (wValue != 0) break; spin_lock(&dev->dev_lock); /* PXA hardware partially handles SET_INTERFACE; @@ -1794,7 +1728,7 @@ goto set_interface_done; } if (dev->dev_config != GS_BULK_CONFIG_ID - && ctrl->wIndex == GS_CONTROL_INTERFACE_ID) { + && wIndex == GS_CONTROL_INTERFACE_ID) { if (dev->dev_notify_ep) { usb_ep_disable(dev->dev_notify_ep); usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc); @@ -1814,21 +1748,21 @@ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) || dev->dev_config == GS_NO_CONFIG_ID) break; - if (ctrl->wIndex >= GS_MAX_NUM_INTERFACES - || (dev->dev_config == GS_BULK_CONFIG_ID - && ctrl->wIndex != GS_BULK_INTERFACE_ID)) { + if (wIndex >= GS_MAX_NUM_INTERFACES + || (dev->dev_config == GS_BULK_CONFIG_ID + && wIndex != GS_BULK_INTERFACE_ID)) { ret = -EDOM; break; } /* no alternate interface settings */ *(u8 *)req->buf = 0; - ret = min(ctrl->wLength, (u16)1); + ret = min(wLength, (u16)1); break; default: printk(KERN_ERR "gs_setup: unknown standard request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, - ctrl->wIndex, ctrl->wLength); + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); break; } @@ -1842,10 +1776,13 @@ struct gs_dev *dev = get_gadget_data(gadget); struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */ struct usb_request *req = dev->dev_ctrl_req; + u16 wIndex = ctrl->wIndex; + u16 wValue = ctrl->wValue; + u16 wLength = ctrl->wLength; switch (ctrl->bRequest) { case USB_CDC_REQ_SET_LINE_CODING: - ret = min(ctrl->wLength, + ret = min(wLength, (u16)sizeof(struct usb_cdc_line_coding)); if (port) { spin_lock(&port->port_lock); @@ -1856,7 +1793,7 @@ case USB_CDC_REQ_GET_LINE_CODING: port = dev->dev_port[0]; /* ACM only has one port */ - ret = min(ctrl->wLength, + ret = min(wLength, (u16)sizeof(struct usb_cdc_line_coding)); if (port) { spin_lock(&port->port_lock); @@ -1871,8 +1808,8 @@ default: printk(KERN_ERR "gs_setup: unknown class request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", - ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, - ctrl->wIndex, ctrl->wLength); + ctrl->bRequestType, ctrl->bRequest, + wValue, wIndex, wLength); break; } @@ -2272,7 +2209,7 @@ memset(port, 0, sizeof(struct gs_port)); port->port_dev = dev; port->port_num = i; - port->port_line_coding.dwDTERate = GS_DEFAULT_DTE_RATE; + port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE); port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT; port->port_line_coding.bParityType = GS_DEFAULT_PARITY; port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS; @@ -2324,6 +2261,7 @@ } spin_unlock_irqrestore(&port->port_lock, flags); } else { + spin_unlock_irqrestore(&port->port_lock, flags); kfree(port); } diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig --- a/drivers/usb/host/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/Kconfig 2005-02-04 22:22:37 -08:00 @@ -7,13 +7,18 @@ default y if ARM # SL-811 default PCI -# many non-PCI hcds implement OHCI +# many non-PCI SOC chips embed OHCI config USB_ARCH_HAS_OHCI boolean + # ARM: default y if SA1111 default y if ARCH_OMAP default y if ARCH_LH7A404 default y if PXA27x + # PPC: + default y if STB03xxx + default y if PPC_MPC52xx + # more: default PCI # @@ -83,6 +88,35 @@ To compile this driver as a module, choose M here: the module will be called ohci-hcd. + +config USB_OHCI_HCD_PPC_SOC + bool "OHCI support for on-chip PPC USB controller" + depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx) + default y + select USB_OHCI_BIG_ENDIAN + ---help--- + Enables support for the USB controller on the MPC52xx or + STB03xxx processor chip. If unsure, say Y. + +config USB_OHCI_HCD_PCI + bool "OHCI support for PCI-bus USB controllers" + depends on USB_OHCI_HCD && PCI && (STB03xxx || PPC_MPC52xx) + default y + select USB_OHCI_LITTLE_ENDIAN + ---help--- + Enables support for PCI-bus plug-in USB controller cards. + If unsure, say Y. + +config USB_OHCI_BIG_ENDIAN + bool + depends on USB_OHCI_HCD + default n + +config USB_OHCI_LITTLE_ENDIAN + bool + depends on USB_OHCI_HCD + default n if STB03xxx || PPC_MPC52xx + default y config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c --- a/drivers/usb/host/ohci-dbg.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/ohci-dbg.c 2005-02-04 22:22:37 -08:00 @@ -138,7 +138,7 @@ ohci_dbg_sw (controller, next, size, "OHCI %d.%d, %s legacy support registers\n", 0x03 & (temp >> 4), (temp & 0x0f), - (temp & 0x10) ? "with" : "NO"); + (temp & 0x0100) ? "with" : "NO"); temp = ohci_readl (controller, ®s->control); ohci_dbg_sw (controller, next, size, @@ -328,7 +328,7 @@ hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff, hc32_to_cpup (ohci, &td->hwBE)); for (i = 0; i < MAXPSW; i++) { - u16 psw = hc16_to_cpup (ohci, &td->hwPSW [i]); + u16 psw = ohci_hwPSW (ohci, td, i); int cc = (psw >> 12) & 0x0f; ohci_dbg (ohci, " psw [%d] = %2x, CC=%x %s=%d\n", i, psw, cc, diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c --- a/drivers/usb/host/ohci-hcd.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/ohci-hcd.c 2005-02-04 22:22:37 -08:00 @@ -148,10 +148,11 @@ #include "ohci-q.c" -/* Some boards don't support per-port power switching */ -static int power_switching = 0; -module_param (power_switching, bool, 0); -MODULE_PARM_DESC (power_switching, "true (not default) to switch port power"); +/* Some boards misreport power switching/overcurrent */ +static int distrust_firmware = 1; +module_param (distrust_firmware, bool, 0); +MODULE_PARM_DESC (distrust_firmware, + "true to distrust firmware power/overcurrent setup"); /* Some boards leave IR set wrongly, since they fail BIOS/SMM handshakes */ static int no_handshake = 0; @@ -532,8 +533,9 @@ // flush the writes (void) ohci_readl (ohci, &ohci->regs->control); msleep(temp); - if (power_switching) { - unsigned ports = roothub_a (ohci) & RH_A_NDP; + temp = roothub_a (ohci); + if (!(temp & RH_A_NPS)) { + unsigned ports = temp & RH_A_NDP; /* power down each port */ for (temp = 0; temp < ports; temp++) @@ -624,21 +626,16 @@ /* NSC 87560 and maybe others */ temp |= RH_A_NOCP; temp &= ~(RH_A_POTPGT | RH_A_NPS); - } else if (power_switching) { - /* act like most external hubs: use per-port power - * switching and overcurrent reporting. - */ - temp &= ~(RH_A_NPS | RH_A_NOCP); - temp |= RH_A_PSM | RH_A_OCPM; - } else { + ohci_writel (ohci, temp, &ohci->regs->roothub.a); + } else if ((ohci->flags & OHCI_QUIRK_AMD756) || distrust_firmware) { /* hub power always on; required for AMD-756 and some * Mac platforms. ganged overcurrent reporting, if any. */ temp |= RH_A_NPS; + ohci_writel (ohci, temp, &ohci->regs->roothub.a); } - ohci_writel (ohci, temp, &ohci->regs->roothub.a); ohci_writel (ohci, RH_HS_LPSC, &ohci->regs->roothub.status); - ohci_writel (ohci, power_switching ? RH_B_PPCM : 0, + ohci_writel (ohci, (temp & RH_A_NPS) ? 0 : RH_B_PPCM, &ohci->regs->roothub.b); // flush those writes (void) ohci_readl (ohci, &ohci->regs->control); @@ -646,7 +643,7 @@ spin_unlock_irq (&ohci->lock); // POTPGT delay is bits 24-31, in 2 ms units. - mdelay ((roothub_a (ohci) >> 23) & 0x1fe); + mdelay ((temp >> 23) & 0x1fe); bus = &ohci_to_hcd(ohci)->self; ohci_to_hcd(ohci)->state = USB_STATE_RUNNING; @@ -901,12 +898,17 @@ #include "ohci-au1xxx.c" #endif +#ifdef CONFIG_USB_OHCI_HCD_PPC_SOC +#include "ohci-ppc-soc.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ || defined (CONFIG_ARCH_LH7A404) \ || defined (CONFIG_PXA27x) \ || defined (CONFIG_SOC_AU1X00) \ + || defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \ ) #error "missing bus glue for ohci-hcd" #endif diff -Nru a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c --- a/drivers/usb/host/ohci-omap.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/ohci-omap.c 2005-02-04 22:22:37 -08:00 @@ -33,11 +33,27 @@ #error "This file is OMAP bus glue. CONFIG_OMAP must be defined." #endif +#ifdef CONFIG_TPS65010 +#include +#else + +#define LOW 0 +#define HIGH 1 + +#define GPIO1 1 + +static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) +{ + return 0; +} + +#endif + extern int usb_disabled(void); extern int ocpi_enable(void); /* - * OHCI clock initialization for OMAP-1510 and 1610 + * OHCI clock initialization for OMAP-1510 and 16xx */ static int omap_ohci_clock_power(int on) { @@ -78,7 +94,8 @@ } /* - * Hardware specific transceiver power on/off + * Board specific gang-switched transceiver power on/off. + * NOTE: OSK supplies power from DC, not battery. */ static int omap_ohci_transceiver_power(int on) { @@ -87,17 +104,15 @@ fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); - else if (machine_is_omap_osk()) { - /* FIXME: GPIO1 -> 1 on the TPS65010 I2C chip */ - } + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, LOW); } else { if (machine_is_omap_innovator() && cpu_is_omap1510()) fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), INNOVATOR_FPGA_CAM_USB_CONTROL); - else if (machine_is_omap_osk()) { - /* FIXME: GPIO1 -> 0 on the TPS65010 I2C chip */ - } + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, HIGH); } return 0; @@ -177,6 +192,7 @@ { struct omap_usb_config *config = pdev->dev.platform_data; int need_transceiver = (config->otg != 0); + int ret; dev_dbg(&pdev->dev, "starting USB Controller\n"); @@ -213,21 +229,44 @@ } #endif - if (machine_is_omap_osk()) { - omap_request_gpio(9); - omap_set_gpio_direction(9, 1); - omap_set_gpio_dataout(9, 1); - } - omap_ohci_clock_power(1); - omap_ohci_transceiver_power(1); - if (cpu_is_omap1510()) { omap_1510_local_bus_power(1); omap_1510_local_bus_init(); } + if ((ret = ohci_init(ohci)) < 0) + return ret; + + /* board-specific power switching and overcurrent support */ + if (machine_is_omap_osk() || machine_is_omap_innovator()) { + u32 rh = roothub_a (ohci); + + /* power switching (ganged by default) */ + rh &= ~RH_A_NPS; + + /* TPS2045 switch for internal transceiver (port 1) */ + if (machine_is_omap_osk()) { + ohci->power_budget = 250; + + rh &= ~RH_A_NOCP; + + /* gpio9 for overcurrent detction */ + omap_cfg_reg(W8_1610_GPIO9); + omap_request_gpio(9); + omap_set_gpio_direction(9, 1 /* IN */); + + /* for paranoia's sake: disable USB.PUEN */ + omap_cfg_reg(W4_USB_HIGHZ); + } + ohci_writel(ohci, rh, &ohci->regs->roothub.a); + // distrust_firmware = 0; + } + + /* FIXME khubd hub requests should manage power switching */ + omap_ohci_transceiver_power(1); + /* board init will have already handled HMC and mux setup. * any external transceiver should already be initialized * too, so all configured ports use the right signaling now. @@ -288,7 +327,8 @@ } if (!request_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1, hcd_name)) { + pdev->resource[0].end - pdev->resource[0].start + 1, + hcd_name)) { dev_dbg(&pdev->dev, "request_mem_region failed\n"); return -EBUSY; } @@ -307,31 +347,31 @@ hcd->regs = (void *)pdev->resource[0].start; hcd->self.controller = &pdev->dev; - retval = omap_start_hc(ohci, pdev); - if (retval < 0) - goto err2; - retval = hcd_buffer_create (hcd); if (retval != 0) { dev_dbg(&pdev->dev, "pool alloc fail\n"); goto err2; } + retval = omap_start_hc(ohci, pdev); + if (retval < 0) + goto err2; + retval = request_irq (hcd->irq, usb_hcd_irq, - SA_INTERRUPT, hcd->driver->description, hcd); + SA_INTERRUPT, hcd_name, hcd); if (retval != 0) { dev_dbg(&pdev->dev, "request_irq failed\n"); retval = -EBUSY; goto err3; } - dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); + dev_info(&pdev->dev, "%s at 0x%p, irq %d\n", + hcd->product_desc, hcd->regs, hcd->irq); hcd->self.bus_name = pdev->dev.bus_id; usb_register_bus (&hcd->self); - if ((retval = driver->start (hcd)) < 0) - { + if ((retval = driver->start (hcd)) < 0) { usb_hcd_omap_remove(hcd, pdev); return retval; } @@ -404,9 +444,6 @@ struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; - if ((ret = ohci_init(ohci)) < 0) - return ret; - config = hcd->self.controller->platform_data; if (config->otg || config->rwc) writel(OHCI_CTRL_RWC, &ohci->regs->control); @@ -499,6 +536,8 @@ struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); int status = -EINVAL; + if (level != SUSPEND_POWER_DOWN) + return 0; if (state <= dev->power.power_state) return 0; @@ -524,6 +563,9 @@ { struct ohci_hcd *ohci = hcd_to_ohci(dev_get_drvdata(dev)); int status = 0; + + if (level != RESUME_POWER_ON) + return 0; switch (dev->power.power_state) { case 0: diff -Nru a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/host/ohci-ppc-soc.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,299 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * (C) Copyright 2003-2005 MontaVista Software Inc. + * + * Bus Glue for PPC On-Chip OHCI driver + * Tested on Freescale MPC5200 and IBM STB04xxx + * + * Modified by Dale Farnsworth from ohci-sa1111.c + * + * This file is licenced under the GPL. + */ + +#include + +static void usb_hcd_ppc_soc_remove(struct usb_hcd *, struct platform_device *); + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + +/** + * usb_hcd_ppc_soc_probe - initialize On-Chip HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + * Store this function in the HCD's struct pci_driver as probe(). + */ +static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, + struct usb_hcd **hcd_out, + struct platform_device *pdev) +{ + int retval; + struct usb_hcd *hcd = 0; + struct ohci_hcd *ohci; + struct resource *res; + int irq; + struct usb_hcd_platform_data *pd = pdev->dev.platform_data; + + pr_debug("initializing PPC-SOC USB Controller\n"); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + pr_debug(__FILE__ ": no irq\n"); + return -ENODEV; + } + irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_debug(__FILE__ ": no reg addr\n"); + return -ENODEV; + } + if (!request_mem_region(res->start, res->end - res->start + 1, + hcd_name)) { + pr_debug(__FILE__ ": request_mem_region failed\n"); + return -EBUSY; + } + + if (pd->start && (retval = pd->start(pdev))) + goto err0; + + hcd = usb_create_hcd(driver); + if (!hcd){ + pr_debug(__FILE__ ": hcd_alloc failed\n"); + retval = -ENOMEM; + goto err1; + } + + ohci = hcd_to_ohci(hcd); + + ohci->flags |= OHCI_BIG_ENDIAN; + + ohci_hcd_init(ohci); + + hcd->irq = irq; + hcd->regs = (struct ohci_regs *) ioremap(res->start, + res->end - res->start + 1); + if (!hcd->regs) { + pr_debug(__FILE__ ": ioremap failed\n"); + retval = -ENOMEM; + goto err2; + } + + hcd->self.controller = &pdev->dev; + + retval = hcd_buffer_create(hcd); + if (retval) { + pr_debug(__FILE__ ": pool alloc fail\n"); + goto err3; + } + + retval = request_irq(hcd->irq, usb_hcd_irq, SA_INTERRUPT, + hcd_name, hcd); + if (retval) { + pr_debug(__FILE__ ": request_irq failed, returned %d\n", + retval); + retval = -EBUSY; + goto err4; + } + + info("%s (PPC-SOC) at 0x%p, irq %d\n", + hcd_name, hcd->regs, hcd->irq); + + hcd->self.bus_name = "PPC-SOC USB"; + + usb_register_bus(&hcd->self); + + if ((retval = driver->start(hcd)) < 0) { + usb_hcd_ppc_soc_remove(hcd, pdev); + return retval; + } + + *hcd_out = hcd; + return 0; + + err4: + hcd_buffer_destroy(hcd); + err3: + iounmap(hcd->regs); + err2: + dev_set_drvdata(&pdev->dev, NULL); + usb_put_hcd(hcd); + err1: + pr_debug("Removing PPC-SOC USB Controller\n"); + if (pd && pd->stop) + pd->stop(pdev); + err0: + release_mem_region(res->start, res->end - res->start + 1); + return retval; +} + + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_ppc_soc_remove - shutdown processing for On-Chip HCDs + * @pdev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_ppc_soc_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +static void usb_hcd_ppc_soc_remove(struct usb_hcd *hcd, struct platform_device *pdev) +{ + struct resource *res; + struct usb_hcd_platform_data *pd = pdev->dev.platform_data; + + pr_debug(__FILE__ ": remove: %s, state %x\n", hcd->self.bus_name, + hcd->state); + if (in_interrupt()) + BUG(); + + hcd->state = USB_STATE_QUIESCING; + + pr_debug("%s: roothub graceful disconnect\n", hcd->self.bus_name); + usb_disconnect(&hcd->self.root_hub); + + hcd->driver->stop(hcd); + hcd->state = USB_STATE_HALT; + + free_irq(hcd->irq, hcd); + hcd_buffer_destroy(hcd); + + usb_deregister_bus(&hcd->self); + + iounmap(hcd->regs); + kfree(hcd); + + pr_debug("stopping PPC-SOC USB Controller\n"); + + if (pd && pd->stop) + pd->stop(pdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); +} + +static int __devinit +ohci_ppc_soc_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + if ((ret = ohci_run(ohci)) < 0) { + err("can't start %s", ohci_to_hcd(ohci)->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver ohci_ppc_soc_hc_driver = { + .description = hcd_name, + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11, + + /* + * basic lifecycle operations + */ + .start = ohci_ppc_soc_start, + .stop = ohci_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_USB_SUSPEND + .hub_suspend = ohci_hub_suspend, + .hub_resume = ohci_hub_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_hcd_ppc_soc_drv_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = NULL; + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = usb_hcd_ppc_soc_probe(&ohci_ppc_soc_hc_driver, &hcd, pdev); + + if (ret == 0) + dev_set_drvdata(dev, hcd); + + return ret; +} + +static int ohci_hcd_ppc_soc_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct usb_hcd *hcd = dev_get_drvdata(dev); + + usb_hcd_ppc_soc_remove(hcd, pdev); + + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct device_driver ohci_hcd_ppc_soc_driver = { + .name = "ppc-soc-ohci", + .bus = &platform_bus_type, + .probe = ohci_hcd_ppc_soc_drv_probe, + .remove = ohci_hcd_ppc_soc_drv_remove, +#if defined(CONFIG_USB_SUSPEND) || defined(CONFIG_PM) + /*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/ + /*.resume = ohci_hcd_ppc_soc_drv_resume,*/ +#endif +}; + +static int __init ohci_hcd_ppc_soc_init(void) +{ + pr_debug(DRIVER_INFO " (PPC SOC)\n"); + pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed), + sizeof(struct td)); + + return driver_register(&ohci_hcd_ppc_soc_driver); +} + +static void __exit ohci_hcd_ppc_soc_cleanup(void) +{ + driver_unregister(&ohci_hcd_ppc_soc_driver); +} + +module_init(ohci_hcd_ppc_soc_init); +module_exit(ohci_hcd_ppc_soc_cleanup); diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/ohci-q.c 2005-02-04 22:22:37 -08:00 @@ -547,7 +547,8 @@ td->hwINFO = cpu_to_hc32 (ohci, info); if (is_iso) { td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000); - td->hwPSW [0] = cpu_to_hc16 (ohci, (data & 0x0FFF) | 0xE000); + *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci, + (data & 0x0FFF) | 0xE000); td->ed->last_iso = info & 0xffff; } else { td->hwCBP = cpu_to_hc32 (ohci, data); @@ -719,10 +720,12 @@ /* ISO ... drivers see per-TD length/status */ if (tdINFO & TD_ISO) { - u16 tdPSW = hc16_to_cpu (ohci, td->hwPSW [0]); + u16 tdPSW = ohci_hwPSW (ohci, td, 0); int dlen = 0; - /* NOTE: assumes FC in tdINFO == 0 (and MAXPSW == 1) */ + /* NOTE: assumes FC in tdINFO == 0, and that + * only the first of 0..MAXPSW psws is used. + */ cc = (tdPSW >> 12) & 0xF; if (tdINFO & TD_CC) /* hc didn't touch? */ diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h --- a/drivers/usb/host/ohci.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/ohci.h 2005-02-04 22:22:37 -08:00 @@ -111,8 +111,10 @@ __hc32 hwNextTD; /* Next TD Pointer */ __hc32 hwBE; /* Memory Buffer End Pointer */ - /* PSW is only for ISO */ -#define MAXPSW 1 /* hardware allows 8 */ + /* PSW is only for ISO. Only 1 PSW entry is used, but on + * big-endian PPC hardware that's the second entry. + */ +#define MAXPSW 2 __hc16 hwPSW [MAXPSW]; /* rest are purely for the driver's use */ @@ -183,7 +185,7 @@ /* * OHCI defines u16 frame_no, followed by u16 zero pad. * Since some processors can't do 16 bit bus accesses, - * portable access must be a 32 bit byteswapped access. + * portable access must be a 32 bits wide. */ __hc32 frame_no; /* current frame number */ __hc32 done_head; /* info returned for an interrupt */ @@ -191,8 +193,6 @@ u8 what [4]; /* spec only identifies 252 bytes :) */ } __attribute__ ((aligned(256))); -#define ohci_frame_no(ohci) ((u16)hc32_to_cpup(ohci,&(ohci)->hcca->frame_no)) - /* * This is the structure of the OHCI controller's memory mapped I/O region. * You must use readl() and writel() (in ) to access these fields!! @@ -550,6 +550,44 @@ static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x) { return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : le32_to_cpup((__force __le32 *)x); +} + +/*-------------------------------------------------------------------------*/ + +/* HCCA frame number is 16 bits, but is accessed as 32 bits since not all + * hardware handles 16 bit reads. That creates a different confusion on + * some big-endian SOC implementations. Same thing happens with PSW access. + */ + +#ifdef CONFIG_STB03xxx +#define OHCI_BE_FRAME_NO_SHIFT 16 +#else +#define OHCI_BE_FRAME_NO_SHIFT 0 +#endif + +static inline u16 ohci_frame_no(const struct ohci_hcd *ohci) +{ + u32 tmp; + if (big_endian(ohci)) { + tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no); + tmp >>= OHCI_BE_FRAME_NO_SHIFT; + } else + tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no); + + return (u16)tmp; +} + +static inline __hc16 *ohci_hwPSWp(const struct ohci_hcd *ohci, + const struct td *td, int index) +{ + return (__hc16 *)(big_endian(ohci) ? + &td->hwPSW[index ^ 1] : &td->hwPSW[index]); +} + +static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci, + const struct td *td, int index) +{ + return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index)); } /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c --- a/drivers/usb/host/uhci-debug.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/uhci-debug.c 2005-02-04 22:22:37 -08:00 @@ -17,6 +17,8 @@ #include "uhci-hcd.h" +static struct dentry *uhci_debugfs_root = NULL; + /* Handle REALLY large printk's so we don't overflow buffers */ static inline void lprintk(char *buf) { @@ -497,8 +499,6 @@ #define MAX_OUTPUT (64 * 1024) -static struct dentry *uhci_debugfs_root = NULL; - struct uhci_debug { int size; char *data; @@ -579,4 +579,9 @@ .read = uhci_debug_read, .release = uhci_debug_release, }; + +#else /* CONFIG_DEBUG_FS */ + +#define uhci_debug_operations (* (struct file_operations *) NULL) + #endif diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/host/uhci-hcd.c 2005-02-04 22:22:37 -08:00 @@ -87,19 +87,9 @@ static char *errbuf; #define ERRBUF_LEN (32 * 1024) -#include "uhci-hub.c" -#include "uhci-debug.c" - static kmem_cache_t *uhci_up_cachep; /* urb_priv */ static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci); -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci); -static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs); -static void uhci_free_pending_qhs(struct uhci_hcd *uhci); -static void uhci_free_pending_tds(struct uhci_hcd *uhci); - static void hc_state_transitions(struct uhci_hcd *uhci); /* If a transfer is still active after this much time, turn off FSBR */ @@ -111,1424 +101,9 @@ /* to make sure it doesn't hog all of the bandwidth */ #define DEPTH_INTERVAL 5 -/* - * Technically, updating td->status here is a race, but it's not really a - * problem. The worst that can happen is that we set the IOC bit again - * generating a spurious interrupt. We could fix this by creating another - * QH and leaving the IOC bit always set, but then we would have to play - * games with the FSBR code to make sure we get the correct order in all - * the cases. I don't think it's worth the effort - */ -static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) -{ - uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); -} - -static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) -{ - uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); -} - -static inline void uhci_moveto_complete(struct uhci_hcd *uhci, - struct urb_priv *urbp) -{ - list_move_tail(&urbp->urb_list, &uhci->complete_list); -} - -static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev) -{ - dma_addr_t dma_handle; - struct uhci_td *td; - - td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle); - if (!td) - return NULL; - - td->dma_handle = dma_handle; - - td->link = UHCI_PTR_TERM; - td->buffer = 0; - - td->frame = -1; - td->dev = dev; - - INIT_LIST_HEAD(&td->list); - INIT_LIST_HEAD(&td->remove_list); - INIT_LIST_HEAD(&td->fl_list); - - usb_get_dev(dev); - - return td; -} - -static inline void uhci_fill_td(struct uhci_td *td, u32 status, - u32 token, u32 buffer) -{ - td->status = cpu_to_le32(status); - td->token = cpu_to_le32(token); - td->buffer = cpu_to_le32(buffer); -} - -/* - * We insert Isochronous URB's directly into the frame list at the beginning - */ -static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum) -{ - framenum &= (UHCI_NUMFRAMES - 1); - - td->frame = framenum; - - /* Is there a TD already mapped there? */ - if (uhci->fl->frame_cpu[framenum]) { - struct uhci_td *ftd, *ltd; - - ftd = uhci->fl->frame_cpu[framenum]; - ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); - - list_add_tail(&td->fl_list, &ftd->fl_list); - - td->link = ltd->link; - wmb(); - ltd->link = cpu_to_le32(td->dma_handle); - } else { - td->link = uhci->fl->frame[framenum]; - wmb(); - uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle); - uhci->fl->frame_cpu[framenum] = td; - } -} - -static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) -{ - /* If it's not inserted, don't remove it */ - if (td->frame == -1 && list_empty(&td->fl_list)) - return; - - if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { - if (list_empty(&td->fl_list)) { - uhci->fl->frame[td->frame] = td->link; - uhci->fl->frame_cpu[td->frame] = NULL; - } else { - struct uhci_td *ntd; - - ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); - uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle); - uhci->fl->frame_cpu[td->frame] = ntd; - } - } else { - struct uhci_td *ptd; - - ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list); - ptd->link = td->link; - } - - wmb(); - td->link = UHCI_PTR_TERM; - - list_del_init(&td->fl_list); - td->frame = -1; -} - -/* - * Inserts a td list into qh. - */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - __le32 *plink; - - /* Ordering isn't important here yet since the QH hasn't been */ - /* inserted into the schedule yet */ - plink = &qh->element; - list_for_each_entry(td, &urbp->td_list, list) { - *plink = cpu_to_le32(td->dma_handle) | breadth; - plink = &td->link; - } - *plink = UHCI_PTR_TERM; -} - -static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) -{ - if (!list_empty(&td->list)) - dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); - if (!list_empty(&td->remove_list)) - dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); - if (!list_empty(&td->fl_list)) - dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); - - if (td->dev) - usb_put_dev(td->dev); - - dma_pool_free(uhci->td_pool, td, td->dma_handle); -} - -static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev) -{ - dma_addr_t dma_handle; - struct uhci_qh *qh; - - qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle); - if (!qh) - return NULL; - - qh->dma_handle = dma_handle; - - qh->element = UHCI_PTR_TERM; - qh->link = UHCI_PTR_TERM; - - qh->dev = dev; - qh->urbp = NULL; - - INIT_LIST_HEAD(&qh->list); - INIT_LIST_HEAD(&qh->remove_list); - - usb_get_dev(dev); - - return qh; -} - -static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) -{ - if (!list_empty(&qh->list)) - dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); - if (!list_empty(&qh->remove_list)) - dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh); - - if (qh->dev) - usb_put_dev(qh->dev); - - dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); -} - -/* - * Append this urb's qh after the last qh in skelqh->list - * - * Note that urb_priv.queue_list doesn't have a separate queue head; - * it's a ring with every element "live". - */ -static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct urb_priv *turbp; - struct uhci_qh *lqh; - - /* Grab the last QH */ - lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); - - /* Point to the next skelqh */ - urbp->qh->link = lqh->link; - wmb(); /* Ordering is important */ - - /* - * Patch QHs for previous endpoint's queued URBs? HC goes - * here next, not to the next skelqh it now points to. - * - * lqh --> td ... --> qh ... --> td --> qh ... --> td - * | | | - * v v v - * +<----------------+-----------------+ - * v - * newqh --> td ... --> td - * | - * v - * ... - * - * The HC could see (and use!) any of these as we write them. - */ - lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - if (lqh->urbp) { - list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) - turbp->qh->link = lqh->link; - } - - list_add_tail(&urbp->qh->list, &skelqh->list); -} - -/* - * Start removal of QH from schedule; it finishes next frame. - * TDs should be unlinked before this is called. - */ -static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) -{ - struct uhci_qh *pqh; - __le32 newlink; - unsigned int age; - - if (!qh) - return; - - /* - * Only go through the hoops if it's actually linked in - */ - if (!list_empty(&qh->list)) { - - /* If our queue is nonempty, make the next URB the head */ - if (!list_empty(&qh->urbp->queue_list)) { - struct urb_priv *nurbp; - - nurbp = list_entry(qh->urbp->queue_list.next, - struct urb_priv, queue_list); - nurbp->queued = 0; - list_add(&nurbp->qh->list, &qh->list); - newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - } else - newlink = qh->link; - - /* Fix up the previous QH's queue to link to either - * the new head of this queue or the start of the - * next endpoint's queue. */ - pqh = list_entry(qh->list.prev, struct uhci_qh, list); - pqh->link = newlink; - if (pqh->urbp) { - struct urb_priv *turbp; - - list_for_each_entry(turbp, &pqh->urbp->queue_list, - queue_list) - turbp->qh->link = newlink; - } - wmb(); - - /* Leave qh->link in case the HC is on the QH now, it will */ - /* continue the rest of the schedule */ - qh->element = UHCI_PTR_TERM; - - list_del_init(&qh->list); - } - - list_del_init(&qh->urbp->queue_list); - qh->urbp = NULL; - - age = uhci_get_current_frame_number(uhci); - if (age != uhci->qh_remove_age) { - uhci_free_pending_qhs(uhci); - uhci->qh_remove_age = age; - } - - /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the QH */ - if (list_empty(&uhci->qh_remove_list)) - uhci_set_next_interrupt(uhci); - - list_add(&qh->remove_list, &uhci->qh_remove_list); -} - -static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - - list_for_each_entry(td, &urbp->td_list, list) { - if (toggle) - td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); - else - td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); - - toggle ^= 1; - } - - return toggle; -} - -/* This function will append one URB's QH to another URB's QH. This is for */ -/* queuing interrupt, control or bulk transfers */ -static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) -{ - struct urb_priv *eurbp, *urbp, *furbp, *lurbp; - struct uhci_td *lltd; - - eurbp = eurb->hcpriv; - urbp = urb->hcpriv; - - /* Find the first URB in the queue */ - furbp = eurbp; - if (eurbp->queued) { - list_for_each_entry(furbp, &eurbp->queue_list, queue_list) - if (!furbp->queued) - break; - } - - lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); - - lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); - - /* Control transfers always start with toggle 0 */ - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), - uhci_fixup_toggle(urb, - uhci_toggle(td_token(lltd)) ^ 1)); - - /* All qh's in the queue need to link to the next queue */ - urbp->qh->link = eurbp->qh->link; - - wmb(); /* Make sure we flush everything */ - - lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; - - list_add_tail(&urbp->queue_list, &furbp->queue_list); - - urbp->queued = 1; -} - -static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp, *nurbp, *purbp, *turbp; - struct uhci_td *pltd; - unsigned int toggle; - - urbp = urb->hcpriv; - - if (list_empty(&urbp->queue_list)) - return; - - nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); - - /* - * Fix up the toggle for the following URBs in the queue. - * Only needed for bulk and interrupt: control and isochronous - * endpoints don't propagate toggles between messages. - */ - if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { - if (!urbp->queued) - /* We just set the toggle in uhci_unlink_generic */ - toggle = usb_gettoggle(urb->dev, - usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - else { - /* If we're in the middle of the queue, grab the */ - /* toggle from the TD previous to us */ - purbp = list_entry(urbp->queue_list.prev, - struct urb_priv, queue_list); - pltd = list_entry(purbp->td_list.prev, - struct uhci_td, list); - toggle = uhci_toggle(td_token(pltd)) ^ 1; - } - - list_for_each_entry(turbp, &urbp->queue_list, queue_list) { - if (!turbp->queued) - break; - toggle = uhci_fixup_toggle(turbp->urb, toggle); - } - - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } - - if (urbp->queued) { - /* We're somewhere in the middle (or end). The case where - * we're at the head is handled in uhci_remove_qh(). */ - purbp = list_entry(urbp->queue_list.prev, struct urb_priv, - queue_list); - - pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); - if (nurbp->queued) - pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; - else - /* The next URB happens to be the beginning, so */ - /* we're the last, end the chain */ - pltd->link = UHCI_PTR_TERM; - } - - /* urbp->queue_list is handled in uhci_remove_qh() */ -} - -static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp; - - urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); - if (!urbp) - return NULL; - - memset((void *)urbp, 0, sizeof(*urbp)); - - urbp->inserttime = jiffies; - urbp->fsbrtime = jiffies; - urbp->urb = urb; - - INIT_LIST_HEAD(&urbp->td_list); - INIT_LIST_HEAD(&urbp->queue_list); - INIT_LIST_HEAD(&urbp->urb_list); - - list_add_tail(&urbp->urb_list, &uhci->urb_list); - - urb->hcpriv = urbp; - - return urbp; -} - -static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - td->urb = urb; - - list_add_tail(&td->list, &urbp->td_list); -} - -static void uhci_remove_td_from_urb(struct uhci_td *td) -{ - if (list_empty(&td->list)) - return; - - list_del_init(&td->list); - - td->urb = NULL; -} - -static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) -{ - struct uhci_td *td, *tmp; - struct urb_priv *urbp; - unsigned int age; - - urbp = (struct urb_priv *)urb->hcpriv; - if (!urbp) - return; - - if (!list_empty(&urbp->urb_list)) - dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " - "or uhci->remove_list!\n", urb); - - age = uhci_get_current_frame_number(uhci); - if (age != uhci->td_remove_age) { - uhci_free_pending_tds(uhci); - uhci->td_remove_age = age; - } - - /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the TD's*/ - if (list_empty(&uhci->td_remove_list)) - uhci_set_next_interrupt(uhci); - - list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { - uhci_remove_td_from_urb(td); - uhci_remove_td(uhci, td); - list_add(&td->remove_list, &uhci->td_remove_list); - } - - urb->hcpriv = NULL; - kmem_cache_free(uhci_up_cachep, urbp); -} - -static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) { - urbp->fsbr = 1; - if (!uhci->fsbr++ && !uhci->fsbrtimeout) - uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; - } -} - -static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - - if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) { - urbp->fsbr = 0; - if (!--uhci->fsbr) - uhci->fsbrtimeout = jiffies + FSBR_DELAY; - } -} - -/* - * Map status to standard result codes - * - * is (td_status(td) & 0xF60000), a.k.a. - * uhci_status_bits(td_status(td)). - * Note: does not include the TD_CTRL_NAK bit. - * is True for output TDs and False for input TDs. - */ -static int uhci_map_status(int status, int dir_out) -{ - if (!status) - return 0; - if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ - return -EPROTO; - if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ - if (dir_out) - return -EPROTO; - else - return -EILSEQ; - } - if (status & TD_CTRL_BABBLE) /* Babble */ - return -EOVERFLOW; - if (status & TD_CTRL_DBUFERR) /* Buffer error */ - return -ENOSR; - if (status & TD_CTRL_STALLED) /* Stalled */ - return -EPIPE; - WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ - return 0; -} - -/* - * Control transfers - */ -static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - struct uhci_qh *qh, *skelqh; - unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - int len = urb->transfer_buffer_length; - dma_addr_t data = urb->transfer_dma; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - - /* 3 errors */ - status = TD_CTRL_ACTIVE | uhci_maxerr(3); - if (urb->dev->speed == USB_SPEED_LOW) - status |= TD_CTRL_LS; - - /* - * Build the TD for the control request setup packet - */ - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(7), - urb->setup_dma); - - /* - * If direction is "send", change the packet ID from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69) and - * set Short Packet Detect (SPD) for all data packets. - */ - if (usb_pipeout(urb->pipe)) - destination ^= (USB_PID_SETUP ^ USB_PID_OUT); - else { - destination ^= (USB_PID_SETUP ^ USB_PID_IN); - status |= TD_CTRL_SPD; - } - - /* - * Build the DATA TD's - */ - while (len > 0) { - int pktsze = len; - - if (pktsze > maxsze) - pktsze = maxsze; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - /* Alternate Data0/1 (start with Data1) */ - destination ^= TD_TOKEN_TOGGLE; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1), - data); - - data += pktsze; - len -= pktsze; - } - - /* - * Build the final TD for control status - */ - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - /* - * It's IN if the pipe is an output pipe or we're not expecting - * data back. - */ - destination &= ~TD_TOKEN_PID_MASK; - if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) - destination |= USB_PID_IN; - else - destination |= USB_PID_OUT; - - destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ - - status &= ~TD_CTRL_SPD; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status | TD_CTRL_IOC, - destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0); - - qh = uhci_alloc_qh(uhci, urb->dev); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); - - /* Low-speed transfers get a different queue, and won't hog the bus. - * Also, some devices enumerate better without FSBR; the easiest way - * to do that is to put URBs on the low-speed queue while the device - * is in the DEFAULT state. */ - if (urb->dev->speed == USB_SPEED_LOW || - urb->dev->state == USB_STATE_DEFAULT) - skelqh = uhci->skel_ls_control_qh; - else { - skelqh = uhci->skel_fs_control_qh; - uhci_inc_fsbr(uhci, urb); - } - - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; -} - -/* - * If control-IN transfer was short, the status packet wasn't sent. - * This routine changes the element pointer in the QH to point at the - * status TD. It's safe to do this even while the QH is live, because - * the hardware only updates the element pointer following a successful - * transfer. The inactive TD for the short packet won't cause an update, - * so the pointer won't get overwritten. The next time the controller - * sees this QH, it will send the status packet. - */ -static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct uhci_td *td; - - urbp->short_control_packet = 1; - - td = list_entry(urbp->td_list.prev, struct uhci_td, list); - urbp->qh->element = cpu_to_le32(td->dma_handle); - - return -EINPROGRESS; -} - - -static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) -{ - struct list_head *tmp, *head; - struct urb_priv *urbp = urb->hcpriv; - struct uhci_td *td; - unsigned int status; - int ret = 0; - - if (list_empty(&urbp->td_list)) - return -EINVAL; - - head = &urbp->td_list; - - if (urbp->short_control_packet) { - tmp = head->prev; - goto status_stage; - } - - tmp = head->next; - td = list_entry(tmp, struct uhci_td, list); - - /* The first TD is the SETUP stage, check the status, but skip */ - /* the count */ - status = uhci_status_bits(td_status(td)); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - if (status) - goto td_error; - - urb->actual_length = 0; - - /* The rest of the TD's (but the last) are data */ - tmp = tmp->next; - while (tmp != head && tmp->next != head) { - unsigned int ctrlstat; - - td = list_entry(tmp, struct uhci_td, list); - tmp = tmp->next; - - ctrlstat = td_status(td); - status = uhci_status_bits(ctrlstat); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - urb->actual_length += uhci_actual_length(ctrlstat); - - if (status) - goto td_error; - - /* Check to see if we received a short packet */ - if (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td))) { - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - ret = -EREMOTEIO; - goto err; - } - - if (uhci_packetid(td_token(td)) == USB_PID_IN) - return usb_control_retrigger_status(uhci, urb); - else - return 0; - } - } - -status_stage: - td = list_entry(tmp, struct uhci_td, list); - - /* Control status stage */ - status = td_status(td); - -#ifdef I_HAVE_BUGGY_APC_BACKUPS - /* APC BackUPS Pro kludge */ - /* It tries to send all of the descriptor instead of the amount */ - /* we requested */ - if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ - status & TD_CTRL_ACTIVE && - status & TD_CTRL_NAK) - return 0; -#endif - - status = uhci_status_bits(status); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - if (status) - goto td_error; - - return 0; - -td_error: - ret = uhci_map_status(status, uhci_packetout(td_token(td))); - -err: - if ((debug == 1 && ret != -EPIPE) || debug > 1) { - /* Some debugging code */ - dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", - __FUNCTION__, status); - - if (errbuf) { - /* Print the chain for debugging purposes */ - uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - - lprintk(errbuf); - } - } - - return ret; -} - -/* - * Common submit for bulk and interrupt - */ -static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh) -{ - struct uhci_td *td; - struct uhci_qh *qh; - unsigned long destination, status; - int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - int len = urb->transfer_buffer_length; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - dma_addr_t data = urb->transfer_dma; - - if (len < 0) - return -EINVAL; - - /* The "pipe" thing contains the destination in bits 8--18 */ - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - status = uhci_maxerr(3) | TD_CTRL_ACTIVE; - if (urb->dev->speed == USB_SPEED_LOW) - status |= TD_CTRL_LS; - if (usb_pipein(urb->pipe)) - status |= TD_CTRL_SPD; - - /* - * Build the DATA TD's - */ - do { /* Allow zero length packets */ - int pktsze = maxsze; - - if (pktsze >= len) { - pktsze = len; - if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) - status &= ~TD_CTRL_SPD; - } - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); - - data += pktsze; - len -= maxsze; - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - } while (len > 0); - - /* - * URB_ZERO_PACKET means adding a 0-length packet, if direction - * is OUT and the transfer_length was an exact multiple of maxsze, - * hence (len = transfer_length - N * maxsze) == 0 - * however, if transfer_length == 0, the zero packet was already - * prepared above. - */ - if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && - !len && urb->transfer_buffer_length) { - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) | - (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), - data); - - usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe)); - } - - /* Set the interrupt-on-completion flag on the last packet. - * A more-or-less typical 4 KB URB (= size of one memory page) - * will require about 3 ms to transfer; that's a little on the - * fast side but not enough to justify delaying an interrupt - * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT - * flag setting. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); - - qh = uhci_alloc_qh(uhci, urb->dev); - if (!qh) - return -ENOMEM; - - urbp->qh = qh; - qh->urbp = urbp; - - /* Always breadth first */ - uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); - - if (eurb) - uhci_append_queued_urb(uhci, eurb, urb); - else - uhci_insert_qh(uhci, skelqh, urb); - - return -EINPROGRESS; -} - -/* - * Common result for bulk and interrupt - */ -static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = urb->hcpriv; - struct uhci_td *td; - unsigned int status = 0; - int ret = 0; - - urb->actual_length = 0; - - list_for_each_entry(td, &urbp->td_list, list) { - unsigned int ctrlstat = td_status(td); - - status = uhci_status_bits(ctrlstat); - if (status & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - urb->actual_length += uhci_actual_length(ctrlstat); - - if (status) - goto td_error; - - if (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td))) { - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - ret = -EREMOTEIO; - goto err; - } else - return 0; - } - } - - return 0; - -td_error: - ret = uhci_map_status(status, uhci_packetout(td_token(td))); - -err: - /* - * Enable this chunk of code if you want to see some more debugging. - * But be careful, it has the tendancy to starve out khubd and prevent - * disconnects from happening successfully if you have a slow debug - * log interface (like a serial console. - */ -#if 0 - if ((debug == 1 && ret != -EPIPE) || debug > 1) { - /* Some debugging code */ - dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", - __FUNCTION__, status); - - if (errbuf) { - /* Print the chain for debugging purposes */ - uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); - - lprintk(errbuf); - } - } -#endif - return ret; -} - -static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) -{ - int ret; - - /* Can't have low-speed bulk transfers */ - if (urb->dev->speed == USB_SPEED_LOW) - return -EINVAL; - - ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); - if (ret == -EINPROGRESS) - uhci_inc_fsbr(uhci, urb); - - return ret; -} - -static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) -{ - /* USB 1.1 interrupt transfers only involve one packet per interval; - * that's the uhci_submit_common() "breadth first" policy. Drivers - * can submit urbs of any length, but longer ones might need many - * intervals to complete. - */ - return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); -} - -/* - * Isochronous transfers - */ -static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) -{ - struct urb *last_urb = NULL; - struct urb_priv *up; - int ret = 0; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - /* look for pending URB's with identical pipe handle */ - if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && - (u->status == -EINPROGRESS) && (u != urb)) { - if (!last_urb) - *start = u->start_frame; - last_urb = u; - } - } - - if (last_urb) { - *end = (last_urb->start_frame + last_urb->number_of_packets * - last_urb->interval) & (UHCI_NUMFRAMES-1); - ret = 0; - } else - ret = -1; /* no previous urb found */ - - return ret; -} - -static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) -{ - int limits; - unsigned int start = 0, end = 0; - - if (urb->number_of_packets > 900) /* 900? Why? */ - return -EFBIG; - - limits = isochronous_find_limits(uhci, urb, &start, &end); - - if (urb->transfer_flags & URB_ISO_ASAP) { - if (limits) - urb->start_frame = - (uhci_get_current_frame_number(uhci) + - 10) & (UHCI_NUMFRAMES - 1); - else - urb->start_frame = end; - } else { - urb->start_frame &= (UHCI_NUMFRAMES - 1); - /* FIXME: Sanity check */ - } - - return 0; -} - -/* - * Isochronous transfers - */ -static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) -{ - struct uhci_td *td; - int i, ret, frame; - int status, destination; - - status = TD_CTRL_ACTIVE | TD_CTRL_IOS; - destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); - - ret = isochronous_find_start(uhci, urb); - if (ret) - return ret; - - frame = urb->start_frame; - for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) { - if (!urb->iso_frame_desc[i].length) - continue; - - td = uhci_alloc_td(uhci, urb->dev); - if (!td) - return -ENOMEM; - - uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1), - urb->transfer_dma + urb->iso_frame_desc[i].offset); - - if (i + 1 >= urb->number_of_packets) - td->status |= cpu_to_le32(TD_CTRL_IOC); - - uhci_insert_td_frame_list(uhci, td, frame); - } - - return -EINPROGRESS; -} - -static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) -{ - struct uhci_td *td; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int status; - int i, ret = 0; - - urb->actual_length = 0; - - i = 0; - list_for_each_entry(td, &urbp->td_list, list) { - int actlength; - unsigned int ctrlstat = td_status(td); - - if (ctrlstat & TD_CTRL_ACTIVE) - return -EINPROGRESS; - - actlength = uhci_actual_length(ctrlstat); - urb->iso_frame_desc[i].actual_length = actlength; - urb->actual_length += actlength; - - status = uhci_map_status(uhci_status_bits(ctrlstat), - usb_pipeout(urb->pipe)); - urb->iso_frame_desc[i].status = status; - if (status) { - urb->error_count++; - ret = status; - } - - i++; - } - - return ret; -} - -static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *up; - - /* We don't match Isoc transfers since they are special */ - if (usb_pipeisoc(urb->pipe)) - return NULL; - - list_for_each_entry(up, &uhci->urb_list, urb_list) { - struct urb *u = up->urb; - - if (u->dev == urb->dev && u->status == -EINPROGRESS) { - /* For control, ignore the direction */ - if (usb_pipecontrol(urb->pipe) && - (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN)) - return u; - else if (u->pipe == urb->pipe) - return u; - } - } - - return NULL; -} - -static int uhci_urb_enqueue(struct usb_hcd *hcd, - struct usb_host_endpoint *ep, - struct urb *urb, int mem_flags) -{ - int ret; - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long flags; - struct urb *eurb; - int bustime; - - spin_lock_irqsave(&uhci->schedule_lock, flags); - - ret = urb->status; - if (ret != -EINPROGRESS) /* URB already unlinked! */ - goto out; - - eurb = uhci_find_urb_ep(uhci, urb); - - if (!uhci_alloc_urb_priv(uhci, urb)) { - ret = -ENOMEM; - goto out; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - ret = uhci_submit_control(uhci, urb, eurb); - break; - case PIPE_INTERRUPT: - if (!eurb) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) - ret = bustime; - else { - ret = uhci_submit_interrupt(uhci, urb, eurb); - if (ret == -EINPROGRESS) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { /* inherit from parent */ - urb->bandwidth = eurb->bandwidth; - ret = uhci_submit_interrupt(uhci, urb, eurb); - } - break; - case PIPE_BULK: - ret = uhci_submit_bulk(uhci, urb, eurb); - break; - case PIPE_ISOCHRONOUS: - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - break; - } - - ret = uhci_submit_isochronous(uhci, urb); - if (ret == -EINPROGRESS) - usb_claim_bandwidth(urb->dev, urb, bustime, 1); - break; - } - - if (ret != -EINPROGRESS) { - /* Submit failed, so delete it from the urb_list */ - struct urb_priv *urbp = urb->hcpriv; - - list_del_init(&urbp->urb_list); - uhci_destroy_urb_priv(uhci, urb); - } else - ret = 0; - -out: - spin_unlock_irqrestore(&uhci->schedule_lock, flags); - return ret; -} - -/* - * Return the result of a transfer - */ -static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) -{ - int ret = -EINPROGRESS; - struct urb_priv *urbp; - - spin_lock(&urb->lock); - - urbp = (struct urb_priv *)urb->hcpriv; - - if (urb->status != -EINPROGRESS) /* URB already dequeued */ - goto out; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - ret = uhci_result_control(uhci, urb); - break; - case PIPE_BULK: - case PIPE_INTERRUPT: - ret = uhci_result_common(uhci, urb); - break; - case PIPE_ISOCHRONOUS: - ret = uhci_result_isochronous(uhci, urb); - break; - } - - if (ret == -EINPROGRESS) - goto out; - urb->status = ret; - - switch (usb_pipetype(urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: - case PIPE_ISOCHRONOUS: - /* Release bandwidth for Interrupt or Isoc. transfers */ - if (urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 1); - uhci_unlink_generic(uhci, urb); - break; - case PIPE_INTERRUPT: - /* Release bandwidth for Interrupt or Isoc. transfers */ - /* Make sure we don't release if we have a queued URB */ - if (list_empty(&urbp->queue_list) && urb->bandwidth) - usb_release_bandwidth(urb->dev, urb, 0); - else - /* bandwidth was passed on to queued URB, */ - /* so don't let usb_unlink_urb() release it */ - urb->bandwidth = 0; - uhci_unlink_generic(uhci, urb); - break; - default: - dev_info(uhci_dev(uhci), "%s: unknown pipe type %d " - "for urb %p\n", - __FUNCTION__, usb_pipetype(urb->pipe), urb); - } - - /* Move it from uhci->urb_list to uhci->complete_list */ - uhci_moveto_complete(uhci, urbp); - -out: - spin_unlock(&urb->lock); -} - -static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) -{ - struct list_head *head; - struct uhci_td *td; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - int prevactive = 0; - - uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ - - /* - * Now we need to find out what the last successful toggle was - * so we can update the local data toggle for the next transfer - * - * There are 2 ways the last successful completed TD is found: - * - * 1) The TD is NOT active and the actual length < expected length - * 2) The TD is NOT active and it's the last TD in the chain - * - * and a third way the first uncompleted TD is found: - * - * 3) The TD is active and the previous TD is NOT active - * - * Control and Isochronous ignore the toggle, so this is safe - * for all types - * - * FIXME: The toggle fixups won't be 100% reliable until we - * change over to using a single queue for each endpoint and - * stop the queue before unlinking. - */ - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - unsigned int ctrlstat = td_status(td); - - if (!(ctrlstat & TD_CTRL_ACTIVE) && - (uhci_actual_length(ctrlstat) < - uhci_expected_length(td_token(td)) || - td->list.next == head)) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td)) ^ 1); - else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) - usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), - uhci_packetout(td_token(td)), - uhci_toggle(td_token(td))); - - prevactive = ctrlstat & TD_CTRL_ACTIVE; - } - - uhci_delete_queued_urb(uhci, urb); - - /* The interrupt loop will reclaim the QH's */ - uhci_remove_qh(uhci, urbp->qh); - urbp->qh = NULL; -} - -static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned long flags; - struct urb_priv *urbp; - unsigned int age; - - spin_lock_irqsave(&uhci->schedule_lock, flags); - urbp = urb->hcpriv; - if (!urbp) /* URB was never linked! */ - goto done; - list_del_init(&urbp->urb_list); - - uhci_unlink_generic(uhci, urb); - - age = uhci_get_current_frame_number(uhci); - if (age != uhci->urb_remove_age) { - uhci_remove_pending_urbps(uhci); - uhci->urb_remove_age = age; - } - - /* If we're the first, set the next interrupt bit */ - if (list_empty(&uhci->urb_remove_list)) - uhci_set_next_interrupt(uhci); - list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); - -done: - spin_unlock_irqrestore(&uhci->schedule_lock, flags); - return 0; -} - -static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) -{ - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - struct list_head *head; - struct uhci_td *td; - int count = 0; - - uhci_dec_fsbr(uhci, urb); - - urbp->fsbr_timeout = 1; - - /* - * Ideally we would want to fix qh->element as well, but it's - * read/write by the HC, so that can introduce a race. It's not - * really worth the hassle - */ - - head = &urbp->td_list; - list_for_each_entry(td, head, list) { - /* - * Make sure we don't do the last one (since it'll have the - * TERM bit set) as well as we skip every so many TD's to - * make sure it doesn't hog the bandwidth - */ - if (td->list.next != head && (count % DEPTH_INTERVAL) == - (DEPTH_INTERVAL - 1)) - td->link |= UHCI_PTR_DEPTH; - - count++; - } - - return 0; -} - -/* - * uhci_get_current_frame_number() - * - * returns the current frame number for a USB bus/controller. - */ -static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci) -{ - return inw(uhci->io_addr + USBFRNUM); -} +#include "uhci-hub.c" +#include "uhci-debug.c" +#include "uhci-q.c" static int init_stall_timer(struct usb_hcd *hcd); @@ -1592,62 +167,6 @@ return 0; } -static void uhci_free_pending_qhs(struct uhci_hcd *uhci) -{ - struct uhci_qh *qh, *tmp; - - list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { - list_del_init(&qh->remove_list); - - uhci_free_qh(uhci, qh); - } -} - -static void uhci_free_pending_tds(struct uhci_hcd *uhci) -{ - struct uhci_td *td, *tmp; - - list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) { - list_del_init(&td->remove_list); - - uhci_free_td(uhci, td); - } -} - -static void -uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) -__releases(uhci->schedule_lock) -__acquires(uhci->schedule_lock) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - - uhci_destroy_urb_priv(uhci, urb); - - spin_unlock(&uhci->schedule_lock); - usb_hcd_giveback_urb(hcd, urb, regs); - spin_lock(&uhci->schedule_lock); -} - -static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct urb_priv *urbp, *tmp; - - list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) { - struct urb *urb = urbp->urb; - - list_del_init(&urbp->urb_list); - uhci_finish_urb(hcd, urb, regs); - } -} - -static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) -{ - - /* Splice the urb_remove_list onto the end of the complete_list */ - list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev); -} - static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -1866,6 +385,14 @@ } } +/* + * returns the current frame number for a USB bus/controller. + */ +static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci) +{ + return inw(uhci->io_addr + USBFRNUM); +} + static int start_hc(struct uhci_hcd *uhci) { unsigned long io_addr = uhci->io_addr; @@ -1906,7 +433,7 @@ } /* - * De-allocate all resources.. + * De-allocate all resources */ static void release_uhci(struct uhci_hcd *uhci) { diff -Nru a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/host/uhci-q.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,1488 @@ +/* + * Universal Host Controller Interface driver for USB. + * + * Maintainer: Alan Stern + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu + */ + +static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb); +static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb); +static void uhci_remove_pending_urbps(struct uhci_hcd *uhci); +static void uhci_free_pending_qhs(struct uhci_hcd *uhci); +static void uhci_free_pending_tds(struct uhci_hcd *uhci); + +/* + * Technically, updating td->status here is a race, but it's not really a + * problem. The worst that can happen is that we set the IOC bit again + * generating a spurious interrupt. We could fix this by creating another + * QH and leaving the IOC bit always set, but then we would have to play + * games with the FSBR code to make sure we get the correct order in all + * the cases. I don't think it's worth the effort + */ +static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci) +{ + uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); +} + +static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) +{ + uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); +} + +static inline void uhci_moveto_complete(struct uhci_hcd *uhci, + struct urb_priv *urbp) +{ + list_move_tail(&urbp->urb_list, &uhci->complete_list); +} + +static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci, struct usb_device *dev) +{ + dma_addr_t dma_handle; + struct uhci_td *td; + + td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle); + if (!td) + return NULL; + + td->dma_handle = dma_handle; + + td->link = UHCI_PTR_TERM; + td->buffer = 0; + + td->frame = -1; + td->dev = dev; + + INIT_LIST_HEAD(&td->list); + INIT_LIST_HEAD(&td->remove_list); + INIT_LIST_HEAD(&td->fl_list); + + usb_get_dev(dev); + + return td; +} + +static inline void uhci_fill_td(struct uhci_td *td, u32 status, + u32 token, u32 buffer) +{ + td->status = cpu_to_le32(status); + td->token = cpu_to_le32(token); + td->buffer = cpu_to_le32(buffer); +} + +/* + * We insert Isochronous URB's directly into the frame list at the beginning + */ +static void uhci_insert_td_frame_list(struct uhci_hcd *uhci, struct uhci_td *td, unsigned framenum) +{ + framenum &= (UHCI_NUMFRAMES - 1); + + td->frame = framenum; + + /* Is there a TD already mapped there? */ + if (uhci->fl->frame_cpu[framenum]) { + struct uhci_td *ftd, *ltd; + + ftd = uhci->fl->frame_cpu[framenum]; + ltd = list_entry(ftd->fl_list.prev, struct uhci_td, fl_list); + + list_add_tail(&td->fl_list, &ftd->fl_list); + + td->link = ltd->link; + wmb(); + ltd->link = cpu_to_le32(td->dma_handle); + } else { + td->link = uhci->fl->frame[framenum]; + wmb(); + uhci->fl->frame[framenum] = cpu_to_le32(td->dma_handle); + uhci->fl->frame_cpu[framenum] = td; + } +} + +static void uhci_remove_td(struct uhci_hcd *uhci, struct uhci_td *td) +{ + /* If it's not inserted, don't remove it */ + if (td->frame == -1 && list_empty(&td->fl_list)) + return; + + if (td->frame != -1 && uhci->fl->frame_cpu[td->frame] == td) { + if (list_empty(&td->fl_list)) { + uhci->fl->frame[td->frame] = td->link; + uhci->fl->frame_cpu[td->frame] = NULL; + } else { + struct uhci_td *ntd; + + ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); + uhci->fl->frame[td->frame] = cpu_to_le32(ntd->dma_handle); + uhci->fl->frame_cpu[td->frame] = ntd; + } + } else { + struct uhci_td *ptd; + + ptd = list_entry(td->fl_list.prev, struct uhci_td, fl_list); + ptd->link = td->link; + } + + wmb(); + td->link = UHCI_PTR_TERM; + + list_del_init(&td->fl_list); + td->frame = -1; +} + +/* + * Inserts a td list into qh. + */ +static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, __le32 breadth) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + __le32 *plink; + + /* Ordering isn't important here yet since the QH hasn't been */ + /* inserted into the schedule yet */ + plink = &qh->element; + list_for_each_entry(td, &urbp->td_list, list) { + *plink = cpu_to_le32(td->dma_handle) | breadth; + plink = &td->link; + } + *plink = UHCI_PTR_TERM; +} + +static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) +{ + if (!list_empty(&td->list)) + dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); + if (!list_empty(&td->remove_list)) + dev_warn(uhci_dev(uhci), "td %p still in remove_list!\n", td); + if (!list_empty(&td->fl_list)) + dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + + if (td->dev) + usb_put_dev(td->dev); + + dma_pool_free(uhci->td_pool, td, td->dma_handle); +} + +static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, struct usb_device *dev) +{ + dma_addr_t dma_handle; + struct uhci_qh *qh; + + qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle); + if (!qh) + return NULL; + + qh->dma_handle = dma_handle; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + qh->dev = dev; + qh->urbp = NULL; + + INIT_LIST_HEAD(&qh->list); + INIT_LIST_HEAD(&qh->remove_list); + + usb_get_dev(dev); + + return qh; +} + +static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + if (!list_empty(&qh->list)) + dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); + if (!list_empty(&qh->remove_list)) + dev_warn(uhci_dev(uhci), "qh %p still in remove_list!\n", qh); + + if (qh->dev) + usb_put_dev(qh->dev); + + dma_pool_free(uhci->qh_pool, qh, qh->dma_handle); +} + +/* + * Append this urb's qh after the last qh in skelqh->list + * + * Note that urb_priv.queue_list doesn't have a separate queue head; + * it's a ring with every element "live". + */ +static void uhci_insert_qh(struct uhci_hcd *uhci, struct uhci_qh *skelqh, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct urb_priv *turbp; + struct uhci_qh *lqh; + + /* Grab the last QH */ + lqh = list_entry(skelqh->list.prev, struct uhci_qh, list); + + /* Point to the next skelqh */ + urbp->qh->link = lqh->link; + wmb(); /* Ordering is important */ + + /* + * Patch QHs for previous endpoint's queued URBs? HC goes + * here next, not to the next skelqh it now points to. + * + * lqh --> td ... --> qh ... --> td --> qh ... --> td + * | | | + * v v v + * +<----------------+-----------------+ + * v + * newqh --> td ... --> td + * | + * v + * ... + * + * The HC could see (and use!) any of these as we write them. + */ + lqh->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; + if (lqh->urbp) { + list_for_each_entry(turbp, &lqh->urbp->queue_list, queue_list) + turbp->qh->link = lqh->link; + } + + list_add_tail(&urbp->qh->list, &skelqh->list); +} + +/* + * Start removal of QH from schedule; it finishes next frame. + * TDs should be unlinked before this is called. + */ +static void uhci_remove_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + struct uhci_qh *pqh; + __le32 newlink; + unsigned int age; + + if (!qh) + return; + + /* + * Only go through the hoops if it's actually linked in + */ + if (!list_empty(&qh->list)) { + + /* If our queue is nonempty, make the next URB the head */ + if (!list_empty(&qh->urbp->queue_list)) { + struct urb_priv *nurbp; + + nurbp = list_entry(qh->urbp->queue_list.next, + struct urb_priv, queue_list); + nurbp->queued = 0; + list_add(&nurbp->qh->list, &qh->list); + newlink = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; + } else + newlink = qh->link; + + /* Fix up the previous QH's queue to link to either + * the new head of this queue or the start of the + * next endpoint's queue. */ + pqh = list_entry(qh->list.prev, struct uhci_qh, list); + pqh->link = newlink; + if (pqh->urbp) { + struct urb_priv *turbp; + + list_for_each_entry(turbp, &pqh->urbp->queue_list, + queue_list) + turbp->qh->link = newlink; + } + wmb(); + + /* Leave qh->link in case the HC is on the QH now, it will */ + /* continue the rest of the schedule */ + qh->element = UHCI_PTR_TERM; + + list_del_init(&qh->list); + } + + list_del_init(&qh->urbp->queue_list); + qh->urbp = NULL; + + age = uhci_get_current_frame_number(uhci); + if (age != uhci->qh_remove_age) { + uhci_free_pending_qhs(uhci); + uhci->qh_remove_age = age; + } + + /* Check to see if the remove list is empty. Set the IOC bit */ + /* to force an interrupt so we can remove the QH */ + if (list_empty(&uhci->qh_remove_list)) + uhci_set_next_interrupt(uhci); + + list_add(&qh->remove_list, &uhci->qh_remove_list); +} + +static int uhci_fixup_toggle(struct urb *urb, unsigned int toggle) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + + list_for_each_entry(td, &urbp->td_list, list) { + if (toggle) + td->token |= cpu_to_le32(TD_TOKEN_TOGGLE); + else + td->token &= ~cpu_to_le32(TD_TOKEN_TOGGLE); + + toggle ^= 1; + } + + return toggle; +} + +/* This function will append one URB's QH to another URB's QH. This is for */ +/* queuing interrupt, control or bulk transfers */ +static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, struct urb *urb) +{ + struct urb_priv *eurbp, *urbp, *furbp, *lurbp; + struct uhci_td *lltd; + + eurbp = eurb->hcpriv; + urbp = urb->hcpriv; + + /* Find the first URB in the queue */ + furbp = eurbp; + if (eurbp->queued) { + list_for_each_entry(furbp, &eurbp->queue_list, queue_list) + if (!furbp->queued) + break; + } + + lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); + + lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); + + /* Control transfers always start with toggle 0 */ + if (!usb_pipecontrol(urb->pipe)) + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), + uhci_fixup_toggle(urb, + uhci_toggle(td_token(lltd)) ^ 1)); + + /* All qh's in the queue need to link to the next queue */ + urbp->qh->link = eurbp->qh->link; + + wmb(); /* Make sure we flush everything */ + + lltd->link = cpu_to_le32(urbp->qh->dma_handle) | UHCI_PTR_QH; + + list_add_tail(&urbp->queue_list, &furbp->queue_list); + + urbp->queued = 1; +} + +static void uhci_delete_queued_urb(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp, *nurbp, *purbp, *turbp; + struct uhci_td *pltd; + unsigned int toggle; + + urbp = urb->hcpriv; + + if (list_empty(&urbp->queue_list)) + return; + + nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); + + /* + * Fix up the toggle for the following URBs in the queue. + * Only needed for bulk and interrupt: control and isochronous + * endpoints don't propagate toggles between messages. + */ + if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { + if (!urbp->queued) + /* We just set the toggle in uhci_unlink_generic */ + toggle = usb_gettoggle(urb->dev, + usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + else { + /* If we're in the middle of the queue, grab the */ + /* toggle from the TD previous to us */ + purbp = list_entry(urbp->queue_list.prev, + struct urb_priv, queue_list); + pltd = list_entry(purbp->td_list.prev, + struct uhci_td, list); + toggle = uhci_toggle(td_token(pltd)) ^ 1; + } + + list_for_each_entry(turbp, &urbp->queue_list, queue_list) { + if (!turbp->queued) + break; + toggle = uhci_fixup_toggle(turbp->urb, toggle); + } + + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), toggle); + } + + if (urbp->queued) { + /* We're somewhere in the middle (or end). The case where + * we're at the head is handled in uhci_remove_qh(). */ + purbp = list_entry(urbp->queue_list.prev, struct urb_priv, + queue_list); + + pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); + if (nurbp->queued) + pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; + else + /* The next URB happens to be the beginning, so */ + /* we're the last, end the chain */ + pltd->link = UHCI_PTR_TERM; + } + + /* urbp->queue_list is handled in uhci_remove_qh() */ +} + +static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp; + + urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); + if (!urbp) + return NULL; + + memset((void *)urbp, 0, sizeof(*urbp)); + + urbp->inserttime = jiffies; + urbp->fsbrtime = jiffies; + urbp->urb = urb; + + INIT_LIST_HEAD(&urbp->td_list); + INIT_LIST_HEAD(&urbp->queue_list); + INIT_LIST_HEAD(&urbp->urb_list); + + list_add_tail(&urbp->urb_list, &uhci->urb_list); + + urb->hcpriv = urbp; + + return urbp; +} + +static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + td->urb = urb; + + list_add_tail(&td->list, &urbp->td_list); +} + +static void uhci_remove_td_from_urb(struct uhci_td *td) +{ + if (list_empty(&td->list)) + return; + + list_del_init(&td->list); + + td->urb = NULL; +} + +static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) +{ + struct uhci_td *td, *tmp; + struct urb_priv *urbp; + unsigned int age; + + urbp = (struct urb_priv *)urb->hcpriv; + if (!urbp) + return; + + if (!list_empty(&urbp->urb_list)) + dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " + "or uhci->remove_list!\n", urb); + + age = uhci_get_current_frame_number(uhci); + if (age != uhci->td_remove_age) { + uhci_free_pending_tds(uhci); + uhci->td_remove_age = age; + } + + /* Check to see if the remove list is empty. Set the IOC bit */ + /* to force an interrupt so we can remove the TD's*/ + if (list_empty(&uhci->td_remove_list)) + uhci_set_next_interrupt(uhci); + + list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { + uhci_remove_td_from_urb(td); + uhci_remove_td(uhci, td); + list_add(&td->remove_list, &uhci->td_remove_list); + } + + urb->hcpriv = NULL; + kmem_cache_free(uhci_up_cachep, urbp); +} + +static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) { + urbp->fsbr = 1; + if (!uhci->fsbr++ && !uhci->fsbrtimeout) + uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; + } +} + +static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) { + urbp->fsbr = 0; + if (!--uhci->fsbr) + uhci->fsbrtimeout = jiffies + FSBR_DELAY; + } +} + +/* + * Map status to standard result codes + * + * is (td_status(td) & 0xF60000), a.k.a. + * uhci_status_bits(td_status(td)). + * Note: does not include the TD_CTRL_NAK bit. + * is True for output TDs and False for input TDs. + */ +static int uhci_map_status(int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -EPROTO; + else + return -EILSEQ; + } + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EOVERFLOW; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ + return 0; +} + +/* + * Control transfers + */ +static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + struct uhci_qh *qh, *skelqh; + unsigned long destination, status; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + dma_addr_t data = urb->transfer_dma; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = TD_CTRL_ACTIVE | uhci_maxerr(3); + if (urb->dev->speed == USB_SPEED_LOW) + status |= TD_CTRL_LS; + + /* + * Build the TD for the control request setup packet + */ + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | uhci_explen(7), + urb->setup_dma); + + /* + * If direction is "send", change the packet ID from SETUP (0x2D) + * to OUT (0xE1). Else change it from SETUP to IN (0x69) and + * set Short Packet Detect (SPD) for all data packets. + */ + if (usb_pipeout(urb->pipe)) + destination ^= (USB_PID_SETUP ^ USB_PID_OUT); + else { + destination ^= (USB_PID_SETUP ^ USB_PID_IN); + status |= TD_CTRL_SPD; + } + + /* + * Build the DATA TD's + */ + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + /* Alternate Data0/1 (start with Data1) */ + destination ^= TD_TOKEN_TOGGLE; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1), + data); + + data += pktsze; + len -= pktsze; + } + + /* + * Build the final TD for control status + */ + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + /* + * It's IN if the pipe is an output pipe or we're not expecting + * data back. + */ + destination &= ~TD_TOKEN_PID_MASK; + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ + + status &= ~TD_CTRL_SPD; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status | TD_CTRL_IOC, + destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0); + + qh = uhci_alloc_qh(uhci, urb->dev); + if (!qh) + return -ENOMEM; + + urbp->qh = qh; + qh->urbp = urbp; + + uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); + + /* Low-speed transfers get a different queue, and won't hog the bus. + * Also, some devices enumerate better without FSBR; the easiest way + * to do that is to put URBs on the low-speed queue while the device + * is in the DEFAULT state. */ + if (urb->dev->speed == USB_SPEED_LOW || + urb->dev->state == USB_STATE_DEFAULT) + skelqh = uhci->skel_ls_control_qh; + else { + skelqh = uhci->skel_fs_control_qh; + uhci_inc_fsbr(uhci, urb); + } + + if (eurb) + uhci_append_queued_urb(uhci, eurb, urb); + else + uhci_insert_qh(uhci, skelqh, urb); + + return -EINPROGRESS; +} + +/* + * If control-IN transfer was short, the status packet wasn't sent. + * This routine changes the element pointer in the QH to point at the + * status TD. It's safe to do this even while the QH is live, because + * the hardware only updates the element pointer following a successful + * transfer. The inactive TD for the short packet won't cause an update, + * so the pointer won't get overwritten. The next time the controller + * sees this QH, it will send the status packet. + */ +static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + + urbp->short_control_packet = 1; + + td = list_entry(urbp->td_list.prev, struct uhci_td, list); + urbp->qh->element = cpu_to_le32(td->dma_handle); + + return -EINPROGRESS; +} + + +static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret = 0; + + if (list_empty(&urbp->td_list)) + return -EINVAL; + + head = &urbp->td_list; + + if (urbp->short_control_packet) { + tmp = head->prev; + goto status_stage; + } + + tmp = head->next; + td = list_entry(tmp, struct uhci_td, list); + + /* The first TD is the SETUP stage, check the status, but skip */ + /* the count */ + status = uhci_status_bits(td_status(td)); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + tmp = tmp->next; + while (tmp != head && tmp->next != head) { + unsigned int ctrlstat; + + td = list_entry(tmp, struct uhci_td, list); + tmp = tmp->next; + + ctrlstat = td_status(td); + status = uhci_status_bits(ctrlstat); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(ctrlstat); + + if (status) + goto td_error; + + /* Check to see if we received a short packet */ + if (uhci_actual_length(ctrlstat) < + uhci_expected_length(td_token(td))) { + if (urb->transfer_flags & URB_SHORT_NOT_OK) { + ret = -EREMOTEIO; + goto err; + } + + if (uhci_packetid(td_token(td)) == USB_PID_IN) + return usb_control_retrigger_status(uhci, urb); + else + return 0; + } + } + +status_stage: + td = list_entry(tmp, struct uhci_td, list); + + /* Control status stage */ + status = td_status(td); + +#ifdef I_HAVE_BUGGY_APC_BACKUPS + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of the amount */ + /* we requested */ + if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK) + return 0; +#endif + + status = uhci_status_bits(status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td_token(td))); + +err: + if ((debug == 1 && ret != -EPIPE) || debug > 1) { + /* Some debugging code */ + dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", + __FUNCTION__, status); + + if (errbuf) { + /* Print the chain for debugging purposes */ + uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); + + lprintk(errbuf); + } + } + + return ret; +} + +/* + * Common submit for bulk and interrupt + */ +static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + dma_addr_t data = urb->transfer_dma; + + if (len < 0) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + status = uhci_maxerr(3) | TD_CTRL_ACTIVE; + if (urb->dev->speed == USB_SPEED_LOW) + status |= TD_CTRL_LS; + if (usb_pipein(urb->pipe)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + do { /* Allow zero length packets */ + int pktsze = maxsze; + + if (pktsze >= len) { + pktsze = len; + if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) + status &= ~TD_CTRL_SPD; + } + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), + data); + + data += pktsze; + len -= maxsze; + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } while (len > 0); + + /* + * URB_ZERO_PACKET means adding a 0-length packet, if direction + * is OUT and the transfer_length was an exact multiple of maxsze, + * hence (len = transfer_length - N * maxsze) == 0 + * however, if transfer_length == 0, the zero packet was already + * prepared above. + */ + if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && + !len && urb->transfer_buffer_length) { + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), + data); + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } + + /* Set the interrupt-on-completion flag on the last packet. + * A more-or-less typical 4 KB URB (= size of one memory page) + * will require about 3 ms to transfer; that's a little on the + * fast side but not enough to justify delaying an interrupt + * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT + * flag setting. */ + td->status |= cpu_to_le32(TD_CTRL_IOC); + + qh = uhci_alloc_qh(uhci, urb->dev); + if (!qh) + return -ENOMEM; + + urbp->qh = qh; + qh->urbp = urbp; + + /* Always breadth first */ + uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); + + if (eurb) + uhci_append_queued_urb(uhci, eurb, urb); + else + uhci_insert_qh(uhci, skelqh, urb); + + return -EINPROGRESS; +} + +/* + * Common result for bulk and interrupt + */ +static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status = 0; + int ret = 0; + + urb->actual_length = 0; + + list_for_each_entry(td, &urbp->td_list, list) { + unsigned int ctrlstat = td_status(td); + + status = uhci_status_bits(ctrlstat); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(ctrlstat); + + if (status) + goto td_error; + + if (uhci_actual_length(ctrlstat) < + uhci_expected_length(td_token(td))) { + if (urb->transfer_flags & URB_SHORT_NOT_OK) { + ret = -EREMOTEIO; + goto err; + } else + return 0; + } + } + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td_token(td))); + +err: + /* + * Enable this chunk of code if you want to see some more debugging. + * But be careful, it has the tendancy to starve out khubd and prevent + * disconnects from happening successfully if you have a slow debug + * log interface (like a serial console. + */ +#if 0 + if ((debug == 1 && ret != -EPIPE) || debug > 1) { + /* Some debugging code */ + dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", + __FUNCTION__, status); + + if (errbuf) { + /* Print the chain for debugging purposes */ + uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); + + lprintk(errbuf); + } + } +#endif + return ret; +} + +static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +{ + int ret; + + /* Can't have low-speed bulk transfers */ + if (urb->dev->speed == USB_SPEED_LOW) + return -EINVAL; + + ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); + if (ret == -EINPROGRESS) + uhci_inc_fsbr(uhci, urb); + + return ret; +} + +static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb) +{ + /* USB 1.1 interrupt transfers only involve one packet per interval; + * that's the uhci_submit_common() "breadth first" policy. Drivers + * can submit urbs of any length, but longer ones might need many + * intervals to complete. + */ + return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]); +} + +/* + * Isochronous transfers + */ +static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end) +{ + struct urb *last_urb = NULL; + struct urb_priv *up; + int ret = 0; + + list_for_each_entry(up, &uhci->urb_list, urb_list) { + struct urb *u = up->urb; + + /* look for pending URB's with identical pipe handle */ + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && + (u->status == -EINPROGRESS) && (u != urb)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets * + last_urb->interval) & (UHCI_NUMFRAMES-1); + ret = 0; + } else + ret = -1; /* no previous urb found */ + + return ret; +} + +static int isochronous_find_start(struct uhci_hcd *uhci, struct urb *urb) +{ + int limits; + unsigned int start = 0, end = 0; + + if (urb->number_of_packets > 900) /* 900? Why? */ + return -EFBIG; + + limits = isochronous_find_limits(uhci, urb, &start, &end); + + if (urb->transfer_flags & URB_ISO_ASAP) { + if (limits) + urb->start_frame = + (uhci_get_current_frame_number(uhci) + + 10) & (UHCI_NUMFRAMES - 1); + else + urb->start_frame = end; + } else { + urb->start_frame &= (UHCI_NUMFRAMES - 1); + /* FIXME: Sanity check */ + } + + return 0; +} + +/* + * Isochronous transfers + */ +static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) +{ + struct uhci_td *td; + int i, ret, frame; + int status, destination; + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + ret = isochronous_find_start(uhci, urb); + if (ret) + return ret; + + frame = urb->start_frame; + for (i = 0; i < urb->number_of_packets; i++, frame += urb->interval) { + if (!urb->iso_frame_desc[i].length) + continue; + + td = uhci_alloc_td(uhci, urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1), + urb->transfer_dma + urb->iso_frame_desc[i].offset); + + if (i + 1 >= urb->number_of_packets) + td->status |= cpu_to_le32(TD_CTRL_IOC); + + uhci_insert_td_frame_list(uhci, td, frame); + } + + return -EINPROGRESS; +} + +static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) +{ + struct uhci_td *td; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + int status; + int i, ret = 0; + + urb->actual_length = 0; + + i = 0; + list_for_each_entry(td, &urbp->td_list, list) { + int actlength; + unsigned int ctrlstat = td_status(td); + + if (ctrlstat & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + actlength = uhci_actual_length(ctrlstat); + urb->iso_frame_desc[i].actual_length = actlength; + urb->actual_length += actlength; + + status = uhci_map_status(uhci_status_bits(ctrlstat), + usb_pipeout(urb->pipe)); + urb->iso_frame_desc[i].status = status; + if (status) { + urb->error_count++; + ret = status; + } + + i++; + } + + return ret; +} + +static struct urb *uhci_find_urb_ep(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *up; + + /* We don't match Isoc transfers since they are special */ + if (usb_pipeisoc(urb->pipe)) + return NULL; + + list_for_each_entry(up, &uhci->urb_list, urb_list) { + struct urb *u = up->urb; + + if (u->dev == urb->dev && u->status == -EINPROGRESS) { + /* For control, ignore the direction */ + if (usb_pipecontrol(urb->pipe) && + (u->pipe & ~USB_DIR_IN) == (urb->pipe & ~USB_DIR_IN)) + return u; + else if (u->pipe == urb->pipe) + return u; + } + } + + return NULL; +} + +static int uhci_urb_enqueue(struct usb_hcd *hcd, + struct usb_host_endpoint *ep, + struct urb *urb, int mem_flags) +{ + int ret; + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + struct urb *eurb; + int bustime; + + spin_lock_irqsave(&uhci->schedule_lock, flags); + + ret = urb->status; + if (ret != -EINPROGRESS) /* URB already unlinked! */ + goto out; + + eurb = uhci_find_urb_ep(uhci, urb); + + if (!uhci_alloc_urb_priv(uhci, urb)) { + ret = -ENOMEM; + goto out; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_submit_control(uhci, urb, eurb); + break; + case PIPE_INTERRUPT: + if (!eurb) { + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) + ret = bustime; + else { + ret = uhci_submit_interrupt(uhci, urb, eurb); + if (ret == -EINPROGRESS) + usb_claim_bandwidth(urb->dev, urb, bustime, 0); + } + } else { /* inherit from parent */ + urb->bandwidth = eurb->bandwidth; + ret = uhci_submit_interrupt(uhci, urb, eurb); + } + break; + case PIPE_BULK: + ret = uhci_submit_bulk(uhci, urb, eurb); + break; + case PIPE_ISOCHRONOUS: + bustime = usb_check_bandwidth(urb->dev, urb); + if (bustime < 0) { + ret = bustime; + break; + } + + ret = uhci_submit_isochronous(uhci, urb); + if (ret == -EINPROGRESS) + usb_claim_bandwidth(urb->dev, urb, bustime, 1); + break; + } + + if (ret != -EINPROGRESS) { + /* Submit failed, so delete it from the urb_list */ + struct urb_priv *urbp = urb->hcpriv; + + list_del_init(&urbp->urb_list); + uhci_destroy_urb_priv(uhci, urb); + } else + ret = 0; + +out: + spin_unlock_irqrestore(&uhci->schedule_lock, flags); + return ret; +} + +/* + * Return the result of a transfer + */ +static void uhci_transfer_result(struct uhci_hcd *uhci, struct urb *urb) +{ + int ret = -EINPROGRESS; + struct urb_priv *urbp; + + spin_lock(&urb->lock); + + urbp = (struct urb_priv *)urb->hcpriv; + + if (urb->status != -EINPROGRESS) /* URB already dequeued */ + goto out; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_result_control(uhci, urb); + break; + case PIPE_BULK: + case PIPE_INTERRUPT: + ret = uhci_result_common(uhci, urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_result_isochronous(uhci, urb); + break; + } + + if (ret == -EINPROGRESS) + goto out; + urb->status = ret; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + case PIPE_ISOCHRONOUS: + /* Release bandwidth for Interrupt or Isoc. transfers */ + if (urb->bandwidth) + usb_release_bandwidth(urb->dev, urb, 1); + uhci_unlink_generic(uhci, urb); + break; + case PIPE_INTERRUPT: + /* Release bandwidth for Interrupt or Isoc. transfers */ + /* Make sure we don't release if we have a queued URB */ + if (list_empty(&urbp->queue_list) && urb->bandwidth) + usb_release_bandwidth(urb->dev, urb, 0); + else + /* bandwidth was passed on to queued URB, */ + /* so don't let usb_unlink_urb() release it */ + urb->bandwidth = 0; + uhci_unlink_generic(uhci, urb); + break; + default: + dev_info(uhci_dev(uhci), "%s: unknown pipe type %d " + "for urb %p\n", + __FUNCTION__, usb_pipetype(urb->pipe), urb); + } + + /* Move it from uhci->urb_list to uhci->complete_list */ + uhci_moveto_complete(uhci, urbp); + +out: + spin_unlock(&urb->lock); +} + +static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) +{ + struct list_head *head; + struct uhci_td *td; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + int prevactive = 0; + + uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + + /* + * Now we need to find out what the last successful toggle was + * so we can update the local data toggle for the next transfer + * + * There are 2 ways the last successful completed TD is found: + * + * 1) The TD is NOT active and the actual length < expected length + * 2) The TD is NOT active and it's the last TD in the chain + * + * and a third way the first uncompleted TD is found: + * + * 3) The TD is active and the previous TD is NOT active + * + * Control and Isochronous ignore the toggle, so this is safe + * for all types + * + * FIXME: The toggle fixups won't be 100% reliable until we + * change over to using a single queue for each endpoint and + * stop the queue before unlinking. + */ + head = &urbp->td_list; + list_for_each_entry(td, head, list) { + unsigned int ctrlstat = td_status(td); + + if (!(ctrlstat & TD_CTRL_ACTIVE) && + (uhci_actual_length(ctrlstat) < + uhci_expected_length(td_token(td)) || + td->list.next == head)) + usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), + uhci_packetout(td_token(td)), + uhci_toggle(td_token(td)) ^ 1); + else if ((ctrlstat & TD_CTRL_ACTIVE) && !prevactive) + usb_settoggle(urb->dev, uhci_endpoint(td_token(td)), + uhci_packetout(td_token(td)), + uhci_toggle(td_token(td))); + + prevactive = ctrlstat & TD_CTRL_ACTIVE; + } + + uhci_delete_queued_urb(uhci, urb); + + /* The interrupt loop will reclaim the QH's */ + uhci_remove_qh(uhci, urbp->qh); + urbp->qh = NULL; +} + +static int uhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned long flags; + struct urb_priv *urbp; + unsigned int age; + + spin_lock_irqsave(&uhci->schedule_lock, flags); + urbp = urb->hcpriv; + if (!urbp) /* URB was never linked! */ + goto done; + list_del_init(&urbp->urb_list); + + uhci_unlink_generic(uhci, urb); + + age = uhci_get_current_frame_number(uhci); + if (age != uhci->urb_remove_age) { + uhci_remove_pending_urbps(uhci); + uhci->urb_remove_age = age; + } + + /* If we're the first, set the next interrupt bit */ + if (list_empty(&uhci->urb_remove_list)) + uhci_set_next_interrupt(uhci); + list_add_tail(&urbp->urb_list, &uhci->urb_remove_list); + +done: + spin_unlock_irqrestore(&uhci->schedule_lock, flags); + return 0; +} + +static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct list_head *head; + struct uhci_td *td; + int count = 0; + + uhci_dec_fsbr(uhci, urb); + + urbp->fsbr_timeout = 1; + + /* + * Ideally we would want to fix qh->element as well, but it's + * read/write by the HC, so that can introduce a race. It's not + * really worth the hassle + */ + + head = &urbp->td_list; + list_for_each_entry(td, head, list) { + /* + * Make sure we don't do the last one (since it'll have the + * TERM bit set) as well as we skip every so many TD's to + * make sure it doesn't hog the bandwidth + */ + if (td->list.next != head && (count % DEPTH_INTERVAL) == + (DEPTH_INTERVAL - 1)) + td->link |= UHCI_PTR_DEPTH; + + count++; + } + + return 0; +} + +static void uhci_free_pending_qhs(struct uhci_hcd *uhci) +{ + struct uhci_qh *qh, *tmp; + + list_for_each_entry_safe(qh, tmp, &uhci->qh_remove_list, remove_list) { + list_del_init(&qh->remove_list); + + uhci_free_qh(uhci, qh); + } +} + +static void uhci_free_pending_tds(struct uhci_hcd *uhci) +{ + struct uhci_td *td, *tmp; + + list_for_each_entry_safe(td, tmp, &uhci->td_remove_list, remove_list) { + list_del_init(&td->remove_list); + + uhci_free_td(uhci, td); + } +} + +static void +uhci_finish_urb(struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs) +__releases(uhci->schedule_lock) +__acquires(uhci->schedule_lock) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + uhci_destroy_urb_priv(uhci, urb); + + spin_unlock(&uhci->schedule_lock); + usb_hcd_giveback_urb(hcd, urb, regs); + spin_lock(&uhci->schedule_lock); +} + +static void uhci_finish_completion(struct usb_hcd *hcd, struct pt_regs *regs) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + struct urb_priv *urbp, *tmp; + + list_for_each_entry_safe(urbp, tmp, &uhci->complete_list, urb_list) { + struct urb *urb = urbp->urb; + + list_del_init(&urbp->urb_list); + uhci_finish_urb(hcd, urb, regs); + } +} + +static void uhci_remove_pending_urbps(struct uhci_hcd *uhci) +{ + + /* Splice the urb_remove_list onto the end of the complete_list */ + list_splice_init(&uhci->urb_remove_list, uhci->complete_list.prev); +} diff -Nru a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c --- a/drivers/usb/image/mdc800.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/image/mdc800.c 2005-02-04 22:22:37 -08:00 @@ -95,6 +95,7 @@ #include #include #include +#include #include #include @@ -330,7 +331,7 @@ { mdc800->camera_request_ready=0; mdc800->irq_woken=1; - wake_up_interruptible (&mdc800->irq_wait); + wake_up (&mdc800->irq_wait); } } @@ -346,19 +347,9 @@ */ static int mdc800_usb_waitForIRQ (int mode, int msec) { - DECLARE_WAITQUEUE(wait, current); - long timeout; - mdc800->camera_request_ready=1+mode; - add_wait_queue(&mdc800->irq_wait, &wait); - timeout = msec*HZ/1000; - while (!mdc800->irq_woken && timeout) - { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = schedule_timeout (timeout); - } - remove_wait_queue(&mdc800->irq_wait, &wait); + wait_event_timeout(mdc800->irq_wait, mdc800->irq_woken, msec*HZ/1000); mdc800->irq_woken = 0; if (mdc800->camera_request_ready>0) @@ -395,7 +386,7 @@ mdc800->state=READY; } mdc800->written = 1; - wake_up_interruptible (&mdc800->write_wait); + wake_up (&mdc800->write_wait); } @@ -423,7 +414,7 @@ err ("request bytes fails (status:%i)", urb->status); } mdc800->downloaded = 1; - wake_up_interruptible (&mdc800->download_wait); + wake_up (&mdc800->download_wait); } @@ -704,8 +695,6 @@ { size_t left=len, sts=len; /* single transfer size */ char __user *ptr = buf; - long timeout; - DECLARE_WAITQUEUE(wait, current); down (&mdc800->io_lock); if (mdc800->state == NOT_CONNECTED) @@ -751,14 +740,8 @@ up (&mdc800->io_lock); return len-left; } - add_wait_queue(&mdc800->download_wait, &wait); - timeout = TO_DOWNLOAD_GET_READY*HZ/1000; - while (!mdc800->downloaded && timeout) - { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = schedule_timeout (timeout); - } - remove_wait_queue(&mdc800->download_wait, &wait); + wait_event_timeout(mdc800->download_wait, mdc800->downloaded, + TO_DOWNLOAD_GET_READY*HZ/1000); mdc800->downloaded = 0; if (mdc800->download_urb->status != 0) { @@ -802,7 +785,6 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, size_t len, loff_t *pos) { size_t i=0; - DECLARE_WAITQUEUE(wait, current); down (&mdc800->io_lock); if (mdc800->state != READY) @@ -856,7 +838,6 @@ if (mdc800->in_count == 8) { int answersize; - long timeout; if (mdc800_usb_waitForIRQ (0,TO_GET_READY)) { @@ -876,14 +857,7 @@ up (&mdc800->io_lock); return -EIO; } - add_wait_queue(&mdc800->write_wait, &wait); - timeout = TO_WRITE_GET_READY*HZ/1000; - while (!mdc800->written && timeout) - { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = schedule_timeout (timeout); - } - remove_wait_queue(&mdc800->write_wait, &wait); + wait_event_timeout(mdc800->write_wait, mdc800->written, TO_WRITE_GET_READY*HZ/1000); mdc800->written = 0; if (mdc800->state == WORKING) { diff -Nru a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c --- a/drivers/usb/input/ati_remote.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/input/ati_remote.c 2005-02-04 22:22:37 -08:00 @@ -94,6 +94,7 @@ #include #include #include +#include /* * Module and Version Information, Module Parameters @@ -396,8 +397,6 @@ */ static int ati_remote_sendpacket(struct ati_remote *ati_remote, u16 cmd, unsigned char *data) { - DECLARE_WAITQUEUE(wait, current); - int timeout = HZ; /* 1 second */ int retval = 0; /* Set up out_urb */ @@ -415,18 +414,10 @@ return retval; } - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&ati_remote->wait, &wait); - - while (timeout && (ati_remote->out_urb->status == -EINPROGRESS) - && !(ati_remote->send_flags & SEND_FLAG_COMPLETE)) { - set_current_state(TASK_INTERRUPTIBLE); - timeout = schedule_timeout(timeout); - rmb(); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&ati_remote->wait, &wait); + wait_event_timeout(ati_remote->wait, + ((ati_remote->out_urb->status != -EINPROGRESS) || + (ati_remote->send_flags & SEND_FLAG_COMPLETE)), + HZ); usb_kill_urb(ati_remote->out_urb); return retval; diff -Nru a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c --- a/drivers/usb/input/hid-core.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/input/hid-core.c 2005-02-04 22:22:37 -08:00 @@ -24,6 +24,7 @@ #include #include #include +#include #undef DEBUG #undef DEBUG_DATA @@ -1268,22 +1269,9 @@ int hid_wait_io(struct hid_device *hid) { - DECLARE_WAITQUEUE(wait, current); - int timeout = 10*HZ; - - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&hid->wait, &wait); - - while (timeout && (test_bit(HID_CTRL_RUNNING, &hid->iofl) || - test_bit(HID_OUT_RUNNING, &hid->iofl))) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = schedule_timeout(timeout); - } - - set_current_state(TASK_RUNNING); - remove_wait_queue(&hid->wait, &wait); - - if (!timeout) { + if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &hid->iofl) && + !test_bit(HID_OUT_RUNNING, &hid->iofl)), + 10*HZ)) { dbg("timeout waiting for ctrl or out queue to clear"); return -1; } diff -Nru a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c --- a/drivers/usb/input/wacom.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/input/wacom.c 2005-02-04 22:22:37 -08:00 @@ -72,7 +72,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v1.30" +#define DRIVER_VERSION "v1.40" #define DRIVER_AUTHOR "Vojtech Pavlik " #define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver" #define DRIVER_LICENSE "GPL" @@ -141,8 +141,10 @@ goto exit; } - if (data[0] != 2) + if (data[0] != 2) { dbg("wacom_pl_irq: received unknown report #%d", data[0]); + goto exit; + } prox = data[1] & 0x40; @@ -233,6 +235,7 @@ if (data[0] != 2) { printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]); + goto exit; } input_regs(dev, regs); @@ -246,9 +249,9 @@ input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20); input_report_key(dev, BTN_TOUCH, data[1] & 0x01); } - input_report_abs(dev, ABS_X, data[3] << 8 | data[2]); - input_report_abs(dev, ABS_Y, data[5] << 8 | data[4]); - input_report_abs(dev, ABS_PRESSURE, (data[6]|data[7] << 8)); + input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2])); + input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4])); + input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6])); input_report_key(dev, BTN_STYLUS, data[1] & 0x02); input_report_key(dev, BTN_STYLUS2, data[1] & 0x10); @@ -283,10 +286,15 @@ goto exit; } + if (data[0] != 2) { + printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]); + goto exit; + } + input_regs(dev, regs); input_report_key(dev, BTN_TOOL_PEN, 1); - input_report_abs(dev, ABS_X, le16_to_cpu(get_unaligned((__le16 *) &data[1]))); - input_report_abs(dev, ABS_Y, le16_to_cpu(get_unaligned((__le16 *) &data[3]))); + input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1])); + input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3])); input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127); input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20)); input_report_key(dev, BTN_STYLUS, (data[5] & 0x40)); @@ -322,12 +330,10 @@ goto exit; } - /* check if we can handle the data */ - if (data[0] == 99) - goto exit; - - if (data[0] != 2) + if (data[0] != 2) { dbg("wacom_graphire_irq: received unknown report #%d", data[0]); + goto exit; + } x = le16_to_cpu(*(__le16 *) &data[2]); y = le16_to_cpu(*(__le16 *) &data[4]); @@ -381,107 +387,171 @@ __FUNCTION__, retval); } -static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) +static int wacom_intuos_inout(struct urb *urb) { struct wacom *wacom = urb->context; unsigned char *data = wacom->data; struct input_dev *dev = &wacom->dev; - unsigned int t; int idx; - int retval; - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); - goto exit; - } - - if (data[0] != 2) - dbg("wacom_intuos_irq: received unknown report #%d", data[0]); - - input_regs(dev, regs); /* tool number */ idx = data[1] & 0x01; - if ((data[1] & 0xfc) == 0xc0) { /* Enter report */ - - wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + /* serial number of the tool */ + /* Enter report */ + if ((data[1] & 0xfc) == 0xc0) + { + /* serial number of the tool */ + wacom->serial[idx] = ((__u32)(data[3] & 0x0f) << 28) + ((__u32)data[4] << 20) + ((__u32)data[5] << 12) + ((__u32)data[6] << 4) + (data[7] >> 4); switch (((__u32)data[2] << 4) | (data[3] >> 4)) { - case 0x812: - case 0x012: wacom->tool[idx] = BTN_TOOL_PENCIL; break; /* Inking pen */ - case 0x822: + case 0x812: /* Inking pen */ + case 0x801: /* Intuos3 Inking pen */ + case 0x012: + wacom->tool[idx] = BTN_TOOL_PENCIL; + break; + case 0x822: /* Pen */ case 0x842: case 0x852: - case 0x022: wacom->tool[idx] = BTN_TOOL_PEN; break; /* Pen */ - case 0x832: - case 0x032: wacom->tool[idx] = BTN_TOOL_BRUSH; break; /* Stroke pen */ - case 0x007: + case 0x823: /* Intuos3 Grip Pen */ + case 0x813: /* Intuos3 Classic Pen */ + case 0x885: /* Intuos3 Marker Pen */ + case 0x022: + wacom->tool[idx] = BTN_TOOL_PEN; + break; + case 0x832: /* Stroke pen */ + case 0x032: + wacom->tool[idx] = BTN_TOOL_BRUSH; + break; + case 0x007: /* Mouse 4D and 2D */ case 0x09c: - case 0x094: wacom->tool[idx] = BTN_TOOL_MOUSE; break; /* Mouse 4D and 2D */ - case 0x096: wacom->tool[idx] = BTN_TOOL_LENS; break; /* Lens cursor */ - case 0x82a: + case 0x094: + case 0x017: /* Intuos3 2D Mouse */ + wacom->tool[idx] = BTN_TOOL_MOUSE; + break; + case 0x096: /* Lens cursor */ + case 0x097: /* Intuos3 Lens cursor */ + wacom->tool[idx] = BTN_TOOL_LENS; + break; + case 0x82a: /* Eraser */ case 0x85a: case 0x91a: case 0xd1a: - case 0x0fa: wacom->tool[idx] = BTN_TOOL_RUBBER; break; /* Eraser */ + case 0x0fa: + case 0x82b: /* Intuos3 Grip Pen Eraser */ + case 0x81b: /* Intuos3 Classic Pen Eraser */ + case 0x91b: /* Intuos3 Airbrush Eraser */ + wacom->tool[idx] = BTN_TOOL_RUBBER; + break; case 0xd12: case 0x912: - case 0x112: wacom->tool[idx] = BTN_TOOL_AIRBRUSH; break; /* Airbrush */ - default: wacom->tool[idx] = BTN_TOOL_PEN; break; /* Unknown tool */ + case 0x112: + case 0x913: /* Intuos3 Airbrush */ + wacom->tool[idx] = BTN_TOOL_AIRBRUSH; + break; /* Airbrush */ + default: /* Unknown tool */ + wacom->tool[idx] = BTN_TOOL_PEN; } - input_report_key(dev, wacom->tool[idx], 1); input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_sync(dev); - goto exit; + return 1; } - if ((data[1] & 0xfe) == 0x80) { /* Exit report */ + /* Exit report */ + if ((data[1] & 0xfe) == 0x80) { input_report_key(dev, wacom->tool[idx], 0); input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); input_sync(dev); - goto exit; + return 1; } - input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); - input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); - input_report_abs(dev, ABS_DISTANCE, data[9]); + return 0; +} - if ((data[1] & 0xb8) == 0xa0) { /* general pen packet */ - input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); - input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); +static void wacom_intuos_general(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + + /* general pen packet */ + if ((data[1] & 0xb8) == 0xa0) + { + t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_PRESSURE, t); + input_report_abs(dev, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); input_report_key(dev, BTN_STYLUS, data[1] & 2); input_report_key(dev, BTN_STYLUS2, data[1] & 4); input_report_key(dev, BTN_TOUCH, t > 10); } - if ((data[1] & 0xbc) == 0xb4) { /* airbrush second packet */ - input_report_abs(dev, ABS_WHEEL, ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); - input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + /* airbrush second packet */ + if ((data[1] & 0xbc) == 0xb4) + { + input_report_abs(dev, ABS_WHEEL, + ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(dev, ABS_TILT_X, + ((data[7] << 1) & 0x7e) | (data[8] >> 7)); input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); } + return; +} + +static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + int idx; + int retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + if (data[0] != 2 && data[0] != 5 && data[0] != 6) { + dbg("wacom_intuos_irq: received unknown report #%d", data[0]); + goto exit; + } + + input_regs(dev, regs); + + /* tool number */ + idx = data[1] & 0x01; + + /* process in/out prox events */ + if (wacom_intuos_inout(urb)) goto exit; + + input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2])); + input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4])); + input_report_abs(dev, ABS_DISTANCE, data[9]); + + /* process general packets */ + wacom_intuos_general(urb); if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) { /* 4D mouse or Lens cursor packets */ if (data[1] & 0x02) { /* Rotation packet */ - input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? - ((__u32)data[6] << 3) | ((data[7] >> 5) & 7): - (-(((__u32)data[6] << 3) | ((data[7] >> 5) & 7))) - 1); + t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); + input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ? ((t - 1) / 2) : -t / 2); } else { @@ -493,9 +563,8 @@ input_report_key(dev, BTN_SIDE, data[8] & 0x20); input_report_key(dev, BTN_EXTRA, data[8] & 0x10); - input_report_abs(dev, ABS_THROTTLE, -((data[8] & 0x08) ? - ((__u32)data[6] << 2) | ((data[7] >> 6) & 3) : - -((__u32)data[6] << 2) | ((data[7] >> 6) & 3))); + t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3); + input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t); } else { if (wacom->tool[idx] == BTN_TOOL_MOUSE) { /* 2D mouse packets */ @@ -527,6 +596,111 @@ __FUNCTION__, retval); } +static void wacom_intuos3_irq(struct urb *urb, struct pt_regs *regs) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + int idx, retval; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + goto exit; + } + + /* check for valid report */ + if (data[0] != 2 && data[0] != 5 && data[0] != 12) + { + printk(KERN_INFO "wacom_intuos3_irq: received unknown report #%d\n", data[0]); + goto exit; + } + + input_regs(dev, regs); + + /* tool index is always 0 here since there is no dual input tool */ + idx = data[1] & 0x01; + + /* pad packets. Works as a second tool and is always in prox */ + if (data[0] == 12) + { + /* initiate the pad as a device */ + if (wacom->tool[1] != BTN_TOOL_FINGER) + { + wacom->tool[1] = BTN_TOOL_FINGER; + input_report_key(dev, wacom->tool[1], 1); + } + input_report_key(dev, BTN_0, (data[5] & 0x01)); + input_report_key(dev, BTN_1, (data[5] & 0x02)); + input_report_key(dev, BTN_2, (data[5] & 0x04)); + input_report_key(dev, BTN_3, (data[5] & 0x08)); + input_report_key(dev, BTN_4, (data[6] & 0x01)); + input_report_key(dev, BTN_5, (data[6] & 0x02)); + input_report_key(dev, BTN_6, (data[6] & 0x04)); + input_report_key(dev, BTN_7, (data[6] & 0x08)); + input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]); + input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]); + input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff); + input_sync(dev); + goto exit; + } + + /* process in/out prox events */ + if (wacom_intuos_inout(urb)) goto exit; + + input_report_abs(dev, ABS_X, ((__u32)data[2] << 9) | ((__u32)data[3] << 1) | ((data[9] >> 1) & 1)); + input_report_abs(dev, ABS_Y, ((__u32)data[4] << 9) | ((__u32)data[5] << 1) | (data[9] & 1)); + input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f)); + + /* process general packets */ + wacom_intuos_general(urb); + + if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) + { + /* Marker pen rotation packet. Reported as wheel due to valuator limitation */ + if (data[1] & 0x02) + { + t = ((__u32)data[6] << 3) | ((data[7] >> 5) & 7); + t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) : + ((t-1) / 2 + 450)) : (450 - t / 2) ; + input_report_abs(dev, ABS_WHEEL, t); + } + + /* 2D mouse packets */ + if (wacom->tool[idx] == BTN_TOOL_MOUSE) + { + input_report_key(dev, BTN_LEFT, data[8] & 0x04); + input_report_key(dev, BTN_MIDDLE, data[8] & 0x08); + input_report_key(dev, BTN_RIGHT, data[8] & 0x10); + input_report_key(dev, BTN_SIDE, data[8] & 0x40); + input_report_key(dev, BTN_EXTRA, data[8] & 0x20); + /* mouse wheel is positive when rolled backwards */ + input_report_rel(dev, REL_WHEEL, ((__u32)((data[8] & 0x02) >> 1) + - (__u32)(data[8] & 0x01))); + } + } + + input_report_key(dev, wacom->tool[idx], 1); + input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + input_sync(dev); + +exit: + retval = usb_submit_urb (urb, GFP_ATOMIC); + if (retval) + err ("%s - usb_submit_urb failed with result %d", + __FUNCTION__, retval); +} + static struct wacom_features wacom_features[] = { { "Wacom Penpartner", 7, 5040, 3780, 255, 32, 0, wacom_penpartner_irq }, { "Wacom Graphire", 8, 10206, 7422, 511, 32, 1, wacom_graphire_irq }, @@ -552,6 +726,9 @@ { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, 2, wacom_intuos_irq }, { "Wacom Volito", 8, 5104, 3712, 511, 32, 1, wacom_graphire_irq }, { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, 3, wacom_ptu_irq }, + { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, 4, wacom_intuos3_irq }, + { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, 4, wacom_intuos3_irq }, + { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, 4, wacom_intuos3_irq }, { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, 2, wacom_intuos_irq }, { } }; @@ -581,6 +758,9 @@ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) }, + { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) }, { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) }, { } }; @@ -651,6 +831,12 @@ wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2); break; + case 4: /* new functions for Intuos3 */ + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER); + wacom->dev.keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7); + wacom->dev.absbit[0] |= BIT(ABS_RX) | BIT(ABS_RY); + /* fall through */ + case 2: wacom->dev.evbit[0] |= BIT(EV_MSC) | BIT(EV_REL); wacom->dev.mscbit[0] |= BIT(MSC_SERIAL); @@ -662,7 +848,7 @@ break; case 3: - wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2); + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER); break; } @@ -674,6 +860,8 @@ wacom->dev.absmax[ABS_TILT_Y] = 127; wacom->dev.absmax[ABS_WHEEL] = 1023; + wacom->dev.absmax[ABS_RX] = 4097; + wacom->dev.absmax[ABS_RY] = 4097; wacom->dev.absmin[ABS_RZ] = -900; wacom->dev.absmax[ABS_RZ] = 899; wacom->dev.absmin[ABS_THROTTLE] = -1023; @@ -712,9 +900,10 @@ input_register_device(&wacom->dev); + /* ask the tablet to report tablet data */ + usb_set_report(intf, 3, 2, rep_data, 2); + /* repeat once (not sure why the first call often fails) */ usb_set_report(intf, 3, 2, rep_data, 2); - usb_set_report(intf, 3, 5, rep_data, 0); - usb_set_report(intf, 3, 6, rep_data, 0); printk(KERN_INFO "input: %s on %s\n", wacom->features->name, path); diff -Nru a/drivers/usb/media/sn9c102.h b/drivers/usb/media/sn9c102.h --- a/drivers/usb/media/sn9c102.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/media/sn9c102.h 2005-02-04 22:22:37 -08:00 @@ -53,11 +53,11 @@ /*****************************************************************************/ #define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" -#define SN9C102_MODULE_AUTHOR "(C) 2004 Luca Risolia" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2005 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.22" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 22) +#define SN9C102_MODULE_VERSION "1:1.24" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 24) enum sn9c102_bridge { BRIDGE_SN9C101 = 0x01, diff -Nru a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c --- a/drivers/usb/media/sn9c102_core.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/media/sn9c102_core.c 2005-02-04 22:22:37 -08:00 @@ -164,8 +164,8 @@ struct v4l2_rect* r = &(cam->sensor->cropcap.bounds); const size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? - (p->width * p->height * p->priv)/8 : - (r->width * r->height * p->priv)/8; + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; @@ -499,6 +499,7 @@ { struct sn9c102_device* cam = urb->context; struct sn9c102_frame_t** f; + size_t imagesize; unsigned long lock_flags; u8 i; int err = 0; @@ -516,8 +517,13 @@ wake_up_interruptible(&cam->wait_stream); } - if ((cam->state & DEV_DISCONNECTED)||(cam->state & DEV_MISCONFIGURED)) + if (cam->state & DEV_DISCONNECTED) + return; + + if (cam->state & DEV_MISCONFIGURED) { + wake_up_interruptible(&cam->wait_frame); return; + } if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) goto resubmit_urb; @@ -526,6 +532,10 @@ (*f) = list_entry(cam->inqueue.next, struct sn9c102_frame_t, frame); + imagesize = (cam->sensor->pix_format.width * + cam->sensor->pix_format.height * + cam->sensor->pix_format.priv) / 8; + for (i = 0; i < urb->number_of_packets; i++) { unsigned int img, len, status; void *pos, *sof, *eof; @@ -560,11 +570,10 @@ if (eof) img = (eof > pos) ? eof - pos - 1 : 0; - if ((*f)->buf.bytesused+img>(*f)->buf.length) { + if ((*f)->buf.bytesused+img > imagesize) { u32 b = (*f)->buf.bytesused + img - - (*f)->buf.length; - img = (*f)->buf.length - - (*f)->buf.bytesused; + imagesize; + img = imagesize - (*f)->buf.bytesused; DBG(3, "Expected EOF not found: " "video frame cut") if (eof) @@ -580,7 +589,7 @@ (*f)->buf.bytesused += img; - if ((*f)->buf.bytesused == (*f)->buf.length || + if ((*f)->buf.bytesused == imagesize || (cam->sensor->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X && eof)) { u32 b = (*f)->buf.bytesused; @@ -1558,7 +1567,8 @@ err = wait_event_interruptible ( cam->wait_frame, (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) ); + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); if (err) { up(&cam->fileop_sem); return err; @@ -1567,6 +1577,10 @@ up(&cam->fileop_sem); return -ENODEV; } + if (cam->state & DEV_MISCONFIGURED) { + up(&cam->fileop_sem); + return -EIO; + } } f = list_entry(cam->outqueue.prev, struct sn9c102_frame_t, frame); @@ -1615,7 +1629,8 @@ } if (cam->io == IO_NONE) { - if (!sn9c102_request_buffers(cam, 2, IO_READ)) { + if (!sn9c102_request_buffers(cam, cam->nreadbuffers, + IO_READ)) { DBG(1, "poll() failed, not enough memory") goto error; } @@ -1729,7 +1744,7 @@ } -static int sn9c102_v4l2_ioctl(struct inode* inode, struct file* filp, +static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); @@ -1970,7 +1985,7 @@ return -EFAULT; } - if (cam->module_param.force_munmap) + if (cam->module_param.force_munmap || cam->io == IO_READ) sn9c102_release_buffers(cam); err = sn9c102_set_crop(cam, rect); @@ -1990,7 +2005,7 @@ s->pix_format.height = rect->height/scale; memcpy(&(s->_rect), rect, sizeof(*rect)); - if (cam->module_param.force_munmap && + if ((cam->module_param.force_munmap || cam->io == IO_READ) && nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { cam->state |= DEV_MISCONFIGURED; @@ -2146,7 +2161,7 @@ return -EFAULT; } - if (cam->module_param.force_munmap) + if (cam->module_param.force_munmap || cam->io == IO_READ) sn9c102_release_buffers(cam); err += sn9c102_set_pix_format(cam, pix); @@ -2168,7 +2183,7 @@ memcpy(pfmt, pix, sizeof(*pix)); memcpy(&(s->_rect), &rect, sizeof(rect)); - if (cam->module_param.force_munmap && + if ((cam->module_param.force_munmap || cam->io == IO_READ) && nbuffers != sn9c102_request_buffers(cam, nbuffers, cam->io)) { cam->state |= DEV_MISCONFIGURED; @@ -2346,11 +2361,14 @@ err = wait_event_interruptible ( cam->wait_frame, (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) ); + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); if (err) return err; if (cam->state & DEV_DISCONNECTED) return -ENODEV; + if (cam->state & DEV_MISCONFIGURED) + return -EIO; } spin_lock_irqsave(&cam->queue_lock, lock_flags); @@ -2495,7 +2513,7 @@ return -EIO; } - err = sn9c102_v4l2_ioctl(inode, filp, cmd, (void __user *)arg); + err = sn9c102_ioctl_v4l2(inode, filp, cmd, (void __user *)arg); up(&cam->fileop_sem); diff -Nru a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig --- a/drivers/usb/misc/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/misc/Kconfig 2005-02-04 22:22:37 -08:00 @@ -137,6 +137,8 @@ See also . +source "drivers/usb/misc/sisusbvga/Kconfig" + config USB_TEST tristate "USB testing driver (DEVELOPMENT)" depends on USB && USB_DEVICEFS && EXPERIMENTAL diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile --- a/drivers/usb/misc/Makefile 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/misc/Makefile 2005-02-04 22:22:37 -08:00 @@ -16,3 +16,5 @@ obj-$(CONFIG_USB_RIO500) += rio500.o obj-$(CONFIG_USB_TEST) += usbtest.o obj-$(CONFIG_USB_USS720) += uss720.o + +obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ \ No newline at end of file diff -Nru a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c --- a/drivers/usb/misc/auerswald.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/misc/auerswald.c 2005-02-04 22:22:37 -08:00 @@ -29,6 +29,7 @@ #include #include #include +#include #undef DEBUG /* include debug macros until it's done */ #include @@ -605,7 +606,6 @@ /* Starts chained urb and waits for completion or timeout */ static int auerchain_start_wait_urb (pauerchain_t acp, struct urb *urb, int timeout, int* actual_length) { - DECLARE_WAITQUEUE (wait, current); auerchain_chs_t chs; int status; @@ -613,26 +613,13 @@ init_waitqueue_head (&chs.wqh); chs.done = 0; - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&chs.wqh, &wait); urb->context = &chs; status = auerchain_submit_urb (acp, urb); - if (status) { + if (status) /* something went wrong */ - set_current_state (TASK_RUNNING); - remove_wait_queue (&chs.wqh, &wait); return status; - } - - while (timeout && !chs.done) - { - timeout = schedule_timeout (timeout); - set_current_state(TASK_UNINTERRUPTIBLE); - rmb(); - } - set_current_state (TASK_RUNNING); - remove_wait_queue (&chs.wqh, &wait); + timeout = wait_event_timeout(chs.wqh, chs.done, timeout); if (!timeout && !chs.done) { if (urb->status != -EINPROGRESS) { /* No callback?!! */ diff -Nru a/drivers/usb/misc/sisusbvga/Kconfig b/drivers/usb/misc/sisusbvga/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/Kconfig 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,14 @@ + +config USB_SISUSBVGA + tristate "USB 2.0 SVGA dongle support (Net2280/SiS315)" + depends on USB && USB_EHCI_HCD + ---help--- + Say Y here if you intend to attach a USB2VGA dongle based on a + Net2280 and a SiS315 chip. + + Note that this device requires a USB 2.0 host controller. It will not + work with USB 1.x controllers. + + To compile this driver as a module, choose M here: the module will be + called sisusb. If unsure, say N. + diff -Nru a/drivers/usb/misc/sisusbvga/Makefile b/drivers/usb/misc/sisusbvga/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/Makefile 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,6 @@ +# +# Makefile for the sisusb driver (if driver is inside kernel tree). +# + +obj-$(CONFIG_USB_SISUSBVGA) += sisusb.o + diff -Nru a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/sisusb.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,3144 @@ +/* + * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles + * + * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific psisusbr written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sisusb.h" + +#define SISUSB_DONTSYNC + +/* Forward declarations / clean-up routines */ + +static struct usb_driver sisusb_driver; + +static DECLARE_MUTEX(disconnect_sem); + +static void +sisusb_free_buffers(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < NUMOBUFS; i++) { + if (sisusb->obuf[i]) { + usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize, + sisusb->obuf[i], sisusb->transfer_dma_out[i]); + sisusb->obuf[i] = NULL; + } + } + if (sisusb->ibuf) { + usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize, + sisusb->ibuf, sisusb->transfer_dma_in); + sisusb->ibuf = NULL; + } +} + +static void +sisusb_free_urbs(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < NUMOBUFS; i++) { + usb_free_urb(sisusb->sisurbout[i]); + sisusb->sisurbout[i] = NULL; + } + usb_free_urb(sisusb->sisurbin); + sisusb->sisurbin = NULL; +} + +/* Level 0: USB transport layer */ + +/* 1. out-bulks */ + +/* out-urb management */ + +/* Return 1 if all free, 0 otherwise */ +static int +sisusb_all_free(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < sisusb->numobufs; i++) { + + if (sisusb->urbstatus[i] & SU_URB_BUSY) + return 0; + + } + + return 1; +} + +/* Kill all busy URBs */ +static void +sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) +{ + int i; + + if (sisusb_all_free(sisusb)) + return; + + for (i = 0; i < sisusb->numobufs; i++) { + + if (sisusb->urbstatus[i] & SU_URB_BUSY) + usb_kill_urb(sisusb->sisurbout[i]); + + } +} + +/* Return 1 if ok, 0 if error (not all complete within timeout) */ +static int +sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) +{ + int timeout = 5 * HZ, i = 1; + + wait_event_timeout(sisusb->wait_q, + (i = sisusb_all_free(sisusb)), + timeout); + + return i; +} + +static int +sisusb_outurb_available(struct sisusb_usb_data *sisusb) +{ + int i; + + for (i = 0; i < sisusb->numobufs; i++) { + + if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0) + return i; + + } + + return -1; +} + +static int +sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) +{ + int i, timeout = 5 * HZ; + + wait_event_timeout(sisusb->wait_q, + ((i = sisusb_outurb_available(sisusb)) >= 0), + timeout); + + return i; +} + +static int +sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) +{ + int i; + + i = sisusb_outurb_available(sisusb); + + if (i >= 0) + sisusb->urbstatus[i] |= SU_URB_ALLOC; + + return i; +} + +static void +sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) +{ + if ((index >= 0) && (index < sisusb->numobufs)) + sisusb->urbstatus[index] &= ~SU_URB_ALLOC; +} + +/* completion callback */ + +static void +sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs) +{ + struct sisusb_urb_context *context = urb->context; + struct sisusb_usb_data *sisusb; + + if (!context) + return; + + sisusb = context->sisusb; + + if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) + return; + +#ifndef SISUSB_DONTSYNC + if (context->actual_length) + *(context->actual_length) += urb->actual_length; +#endif + + sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY; + wake_up(&sisusb->wait_q); +} + +static int +sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data, + int len, int *actual_length, int timeout, unsigned int tflags, + dma_addr_t transfer_dma) +{ + struct urb *urb = sisusb->sisurbout[index]; + int retval, byteswritten = 0; + + /* Set up URB */ + urb->transfer_flags = 0; + + usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, + sisusb_bulk_completeout, &sisusb->urbout_context[index]); + + urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK); + urb->actual_length = 0; + + if ((urb->transfer_dma = transfer_dma)) + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* Set up context */ + sisusb->urbout_context[index].actual_length = (timeout) ? + NULL : actual_length; + + /* Declare this urb/buffer in use */ + sisusb->urbstatus[index] |= SU_URB_BUSY; + + /* Submit URB */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + + /* If OK, and if timeout > 0, wait for completion */ + if ((retval == 0) && timeout) { + wait_event_timeout(sisusb->wait_q, + (!(sisusb->urbstatus[index] & SU_URB_BUSY)), + timeout); + if (sisusb->urbstatus[index] & SU_URB_BUSY) { + /* URB timed out... kill it and report error */ + usb_kill_urb(urb); + retval = -ETIMEDOUT; + } else { + /* Otherwise, report urb status */ + retval = urb->status; + byteswritten = urb->actual_length; + } + } + + if (actual_length) + *actual_length = byteswritten; + + return retval; +} + +/* 2. in-bulks */ + +/* completion callback */ + +static void +sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs) +{ + struct sisusb_usb_data *sisusb = urb->context; + + if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) + return; + + sisusb->completein = 1; + wake_up(&sisusb->wait_q); +} + +static int +sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len, + int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma) +{ + struct urb *urb = sisusb->sisurbin; + int retval, readbytes = 0; + + urb->transfer_flags = 0; + + usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, + sisusb_bulk_completein, sisusb); + + urb->transfer_flags |= (tflags | URB_ASYNC_UNLINK); + urb->actual_length = 0; + + if ((urb->transfer_dma = transfer_dma)) + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + sisusb->completein = 0; + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval == 0) { + wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout); + if (!sisusb->completein) { + /* URB timed out... kill it and report error */ + usb_kill_urb(urb); + retval = -ETIMEDOUT; + } else { + /* URB completed within timout */ + retval = urb->status; + readbytes = urb->actual_length; + } + } + + if (actual_length) + *actual_length = readbytes; + + return retval; +} + + +/* Level 1: */ + +/* Send a bulk message of variable size + * + * To copy the data from userspace, give pointer to "userbuffer", + * to copy from (non-DMA) kernel memory, give "kernbuffer". If + * both of these are NULL, it is assumed, that the transfer + * buffer "sisusb->obuf[index]" is set up with the data to send. + * Index is ignored if either kernbuffer or userbuffer is set. + * If async is nonzero, URBs will be sent without waiting for + * completion of the previous URB. + * + * (return 0 on success) + */ + +static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, + char *kernbuffer, const char __user *userbuffer, int index, + ssize_t *bytes_written, unsigned int tflags, int async) +{ + int result = 0, retry, count = len; + int passsize, thispass, transferred_len = 0; + int fromuser = (userbuffer != NULL) ? 1 : 0; + int fromkern = (kernbuffer != NULL) ? 1 : 0; + unsigned int pipe; + char *buffer; + + (*bytes_written) = 0; + + /* Sanity check */ + if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) + return -ENODEV; + + /* If we copy data from kernel or userspace, force the + * allocation of a buffer/urb. If we have the data in + * the transfer buffer[index] already, reuse the buffer/URB + * if the length is > buffer size. (So, transmitting + * large data amounts directly from the transfer buffer + * treats the buffer as a ring buffer. However, we need + * to sync in this case.) + */ + if (fromuser || fromkern) + index = -1; + else if (len > sisusb->obufsize) + async = 0; + + pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep); + + do { + passsize = thispass = (sisusb->obufsize < count) ? + sisusb->obufsize : count; + + if (index < 0) + index = sisusb_get_free_outbuf(sisusb); + + if (index < 0) + return -EIO; + + buffer = sisusb->obuf[index]; + + if (fromuser) { + + if (copy_from_user(buffer, userbuffer, passsize)) + return -EFAULT; + + userbuffer += passsize; + + } else if (fromkern) { + + memcpy(buffer, kernbuffer, passsize); + kernbuffer += passsize; + + } + + retry = 5; + while (thispass) { + + if (!sisusb->sisusb_dev) + return -ENODEV; + + result = sisusb_bulkout_msg(sisusb, + index, + pipe, + buffer, + thispass, + &transferred_len, + async ? 0 : 5 * HZ, + tflags, + sisusb->transfer_dma_out[index]); + + if (result == -ETIMEDOUT) { + + /* Will not happen if async */ + if (!retry--) + return -ETIME; + + continue; + + } else if ((result == 0) && !async && transferred_len) { + + thispass -= transferred_len; + if (thispass) { + if (sisusb->transfer_dma_out) { + /* If DMA, copy remaining + * to beginning of buffer + */ + memcpy(buffer, + buffer + transferred_len, + thispass); + } else { + /* If not DMA, simply increase + * the pointer + */ + buffer += transferred_len; + } + } + + } else + break; + }; + + if (result) + return result; + + (*bytes_written) += passsize; + count -= passsize; + + /* Force new allocation in next iteration */ + if (fromuser || fromkern) + index = -1; + + } while (count > 0); + + if (async) { +#ifdef SISUSB_DONTSYNC + (*bytes_written) = len; + /* Some URBs/buffers might be busy */ +#else + sisusb_wait_all_out_complete(sisusb); + (*bytes_written) = transferred_len; + /* All URBs and all buffers are available */ +#endif + } + + return ((*bytes_written) == len) ? 0 : -EIO; +} + +/* Receive a bulk message of variable size + * + * To copy the data to userspace, give pointer to "userbuffer", + * to copy to kernel memory, give "kernbuffer". One of them + * MUST be set. (There is no technique for letting the caller + * read directly from the ibuf.) + * + */ + +static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, + void *kernbuffer, char __user *userbuffer, ssize_t *bytes_read, + unsigned int tflags) +{ + int result = 0, retry, count = len; + int bufsize, thispass, transferred_len; + unsigned int pipe; + char *buffer; + + (*bytes_read) = 0; + + /* Sanity check */ + if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) + return -ENODEV; + + pipe = usb_rcvbulkpipe(sisusb->sisusb_dev, ep); + buffer = sisusb->ibuf; + bufsize = sisusb->ibufsize; + + retry = 5; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return -EIO; +#endif + + while (count > 0) { + + if (!sisusb->sisusb_dev) + return -ENODEV; + + thispass = (bufsize < count) ? bufsize : count; + + result = sisusb_bulkin_msg(sisusb, + pipe, + buffer, + thispass, + &transferred_len, + 5 * HZ, + tflags, + sisusb->transfer_dma_in); + + if (transferred_len) + thispass = transferred_len; + + else if (result == -ETIMEDOUT) { + + if (!retry--) + return -ETIME; + + continue; + + } else + return -EIO; + + + if (thispass) { + + (*bytes_read) += thispass; + count -= thispass; + + if (userbuffer) { + + if (copy_to_user(userbuffer, buffer, thispass)) + return -EFAULT; + + userbuffer += thispass; + + } else { + + memcpy(kernbuffer, buffer, thispass); + kernbuffer += thispass; + + } + + } + + } + + return ((*bytes_read) == len) ? 0 : -EIO; +} + +static int sisusb_send_packet(struct sisusb_usb_data *sisusb, int len, + struct sisusb_packet *packet) +{ + int ret; + int bytes_transferred = 0; + __le32 tmp; + + if (len == 6) + packet->data = 0; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return 1; +#endif + + /* Eventually correct endianness */ + SISUSB_CORRECT_ENDIANNESS_PACKET(packet); + + /* 1. send the packet */ + ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_GFX_OUT, len, + (char *)packet, NULL, 0, &bytes_transferred, 0, 0); + + if ((ret == 0) && (len == 6)) { + + /* 2. if packet len == 6, it means we read, so wait for 32bit + * return value and write it to packet->data + */ + ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_GFX_IN, 4, + (char *)&tmp, NULL, &bytes_transferred, 0); + + packet->data = le32_to_cpu(tmp); + } + + return ret; +} + +static int sisusb_send_bridge_packet(struct sisusb_usb_data *sisusb, int len, + struct sisusb_packet *packet, + unsigned int tflags) +{ + int ret; + int bytes_transferred = 0; + __le32 tmp; + + if (len == 6) + packet->data = 0; + +#ifdef SISUSB_DONTSYNC + if (!(sisusb_wait_all_out_complete(sisusb))) + return 1; +#endif + + /* Eventually correct endianness */ + SISUSB_CORRECT_ENDIANNESS_PACKET(packet); + + /* 1. send the packet */ + ret = sisusb_send_bulk_msg(sisusb, SISUSB_EP_BRIDGE_OUT, len, + (char *)packet, NULL, 0, &bytes_transferred, tflags, 0); + + if ((ret == 0) && (len == 6)) { + + /* 2. if packet len == 6, it means we read, so wait for 32bit + * return value and write it to packet->data + */ + ret = sisusb_recv_bulk_msg(sisusb, SISUSB_EP_BRIDGE_IN, 4, + (char *)&tmp, NULL, &bytes_transferred, 0); + + packet->data = le32_to_cpu(tmp); + } + + return ret; +} + +/* access video memory and mmio (return 0 on success) */ + +/* Low level */ + +/* The following routines assume being used to transfer byte, word, + * long etc. + * This means that they assume "data" in machine endianness format. + */ + +static int sisusb_write_memio_byte(struct sisusb_usb_data *sisusb, int type, + u32 addr, u8 data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = (1 << (addr & 3)) | (type << 6); + packet.address = addr & ~3; + packet.data = data << ((addr & 3) << 3); + ret = sisusb_send_packet(sisusb, 10, &packet); + return ret; +} + +static int sisusb_write_memio_word(struct sisusb_usb_data *sisusb, int type, + u32 addr, u16 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0003; + packet.data = (u32)data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x0006; + packet.data = (u32)data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = (u32)data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = (u32)data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (u32)data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +static int sisusb_write_memio_24bit(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0007; + packet.data = data & 0x00ffffff; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 16) & 0x00ff; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = (data >> 8) & 0xffff; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +static int sisusb_write_memio_long(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x000f; + packet.data = data; + ret = sisusb_send_packet(sisusb, 10, &packet); + break; + case 1: + packet.header = (type << 6) | 0x000e; + packet.data = data << 8; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + packet.data = data >> 24; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 2: + packet.header = (type << 6) | 0x000c; + packet.data = data << 16; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + packet.data = data >> 16; + ret |= sisusb_send_packet(sisusb, 10, &packet); + break; + case 3: + packet.header = (type << 6) | 0x0008; + packet.data = data << 24; + ret = sisusb_send_packet(sisusb, 10, &packet); + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + packet.data = data >> 8; + ret |= sisusb_send_packet(sisusb, 10, &packet); + } + + return ret; +} + +/* The xxx_bulk routines copy a buffer of variable size. They treat the + * buffer as chars, therefore lsb/msb has to be corrected if using the + * byte/word/long/etc routines for speed-up + * + * If data is from userland, set "userbuffer" (and clear "kernbuffer"), + * if data is in kernel space, set "kernbuffer" (and clear "userbuffer"); + * if neither "kernbuffer" nor "userbuffer" are given, it is assumed + * that the data already is in the transfer buffer "sisusb->obuf[index]". + */ + +static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, + char *kernbuffer, int length, + const char __user *userbuffer, int index, + ssize_t *bytes_written) +{ + struct sisusb_packet packet; + int ret = 0; + static int msgcount = 0; + u8 swap8, fromkern = kernbuffer ? 1 : 0; + u16 swap16; + u32 swap32, flag = (length >> 28) & 1; + char buf[4]; + + /* if neither kernbuffer not userbuffer are given, assume + * data in obuf + */ + if (!fromkern && !userbuffer) + kernbuffer = sisusb->obuf[index]; + + (*bytes_written = 0); + + length &= 0x00ffffff; + + while (length) { + + switch (length) { + + case 0: + return ret; + + case 1: + if (userbuffer) { + if (get_user(swap8, (u8 __user *)userbuffer)) + return -EFAULT; + } else + swap8 = kernbuffer[0]; + + ret = sisusb_write_memio_byte(sisusb, + SISUSB_TYPE_MEM, + addr, swap8); + + if (!ret) + (*bytes_written)++; + + return ret; + + case 2: + if (userbuffer) { + if (get_user(swap16, (u16 __user *)userbuffer)) + return -EFAULT; + } else + swap16 = (kernbuffer[0] << 8) | kernbuffer[1]; + + ret = sisusb_write_memio_word(sisusb, + SISUSB_TYPE_MEM, + addr, + swap16); + + if (!ret) + (*bytes_written) += 2; + + return ret; + + case 3: + if (userbuffer) { + if (copy_from_user(&buf, userbuffer, 3)) + return -EFAULT; + + swap32 = (buf[0] << 16) | + (buf[1] << 8) | + buf[2]; + } else + swap32 = (kernbuffer[0] << 16) | + (kernbuffer[1] << 8) | + kernbuffer[2]; + + ret = sisusb_write_memio_24bit(sisusb, + SISUSB_TYPE_MEM, + addr, + swap32); + + if (!ret) + (*bytes_written) += 3; + + return ret; + + case 4: + if (userbuffer) { + if (get_user(swap32, (u32 __user *)userbuffer)) + return -EFAULT; + } else + swap32 = (kernbuffer[0] << 24) | + (kernbuffer[1] << 16) | + (kernbuffer[2] << 8) | + kernbuffer[3]; + + ret = sisusb_write_memio_long(sisusb, + SISUSB_TYPE_MEM, + addr, + swap32); + if (!ret) + (*bytes_written) += 4; + + return ret; + + default: + if ((length & ~3) > 0x10000) { + + packet.header = 0x001f; + packet.address = 0x000001d4; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001d0; + packet.data = (length & ~3); + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001c0; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + NULL, userbuffer, 0, + bytes_written, 0, 1); + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + kernbuffer, NULL, 0, + bytes_written, 0, 1); + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_LBULK_OUT, + (length & ~3), + NULL, NULL, index, + bytes_written, 0, 1); + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } + + } else { + + packet.header = 0x001f; + packet.address = 0x00000194; + packet.data = addr; + ret = sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x00000190; + packet.data = (length & ~3); + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (sisusb->flagb0 != 0x16) { + packet.header = 0x001f; + packet.address = 0x00000180; + packet.data = flag | 0x16; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + sisusb->flagb0 = 0x16; + } + if (userbuffer) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + NULL, userbuffer, 0, + bytes_written, 0, 1); + userbuffer += (*bytes_written); + } else if (fromkern) { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + kernbuffer, NULL, 0, + bytes_written, 0, 1); + kernbuffer += (*bytes_written); + } else { + ret |= sisusb_send_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_OUT, + (length & ~3), + NULL, NULL, index, + bytes_written, 0, 1); + kernbuffer += ((*bytes_written) & + (sisusb->obufsize-1)); + } + } + if (ret) { + msgcount++; + if (msgcount < 500) + printk(KERN_ERR + "sisusbvga[%d]: Wrote %d of " + "%d bytes, error %d\n", + sisusb->minor, *bytes_written, + length, ret); + else if (msgcount == 500) + printk(KERN_ERR + "sisusbvga[%d]: Too many errors" + ", logging stopped\n", + sisusb->minor); + } + addr += (*bytes_written); + length -= (*bytes_written); + } + + if (ret) + break; + + } + + return ret ? -EIO : 0; +} + +static int sisusb_read_memio_byte(struct sisusb_usb_data *sisusb, int type, + u32 addr, u8 *data) +{ + struct sisusb_packet packet; + int ret; + + CLEARPACKET(&packet); + packet.header = (1 << (addr & 3)) | (type << 6); + packet.address = addr & ~3; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u8)(packet.data >> ((addr & 3) << 3)); + return ret; +} + +static int sisusb_read_memio_word(struct sisusb_usb_data *sisusb, int type, + u32 addr, u16 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + CLEARPACKET(&packet); + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0003; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data); + break; + case 1: + packet.header = (type << 6) | 0x0006; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 8); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = (u16)(packet.data >> 24); + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (u16)(packet.data << 8); + } + + return ret; +} + +static int sisusb_read_memio_24bit(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x0007; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data & 0x00ffffff; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xff) << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= ((packet.data & 0xffff) << 8); + } + + return ret; +} + +static int sisusb_read_memio_long(struct sisusb_usb_data *sisusb, int type, + u32 addr, u32 *data) +{ + struct sisusb_packet packet; + int ret = 0; + + packet.address = addr & ~3; + + switch (addr & 3) { + case 0: + packet.header = (type << 6) | 0x000f; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data; + break; + case 1: + packet.header = (type << 6) | 0x000e; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 8; + packet.header = (type << 6) | 0x0001; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 24); + break; + case 2: + packet.header = (type << 6) | 0x000c; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 16; + packet.header = (type << 6) | 0x0003; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 16); + break; + case 3: + packet.header = (type << 6) | 0x0008; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data >> 24; + packet.header = (type << 6) | 0x0007; + packet.address = (addr & ~3) + 4; + ret |= sisusb_send_packet(sisusb, 6, &packet); + *data |= (packet.data << 8); + } + + return ret; +} + +static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, + char *kernbuffer, int length, + char __user *userbuffer, ssize_t *bytes_read) +{ + int ret = 0; + char buf[4]; + u16 swap16; + u32 swap32; + + (*bytes_read = 0); + + length &= 0x00ffffff; + + while (length) { + + switch (length) { + + case 0: + return ret; + + case 1: + + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, + addr, &buf[0]); + if (!ret) { + (*bytes_read)++; + if (userbuffer) { + if (put_user(buf[0], + (u8 __user *)userbuffer)) { + return -EFAULT; + } + } else { + kernbuffer[0] = buf[0]; + } + } + return ret; + + case 2: + ret |= sisusb_read_memio_word(sisusb, SISUSB_TYPE_MEM, + addr, &swap16); + if (!ret) { + (*bytes_read) += 2; + if (userbuffer) { + if (put_user(swap16, + (u16 __user *)userbuffer)) + return -EFAULT; + } else { + kernbuffer[0] = swap16 >> 8; + kernbuffer[1] = swap16 & 0xff; + } + } + return ret; + + case 3: + ret |= sisusb_read_memio_24bit(sisusb, SISUSB_TYPE_MEM, + addr, &swap32); + if (!ret) { + (*bytes_read) += 3; + buf[0] = (swap32 >> 16) & 0xff; + buf[1] = (swap32 >> 8) & 0xff; + buf[2] = swap32 & 0xff; + if (userbuffer) { + if (copy_to_user(userbuffer, &buf[0], 3)) + return -EFAULT; + } else { + kernbuffer[0] = buf[0]; + kernbuffer[1] = buf[1]; + kernbuffer[2] = buf[2]; + } + } + return ret; + + default: + ret |= sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, + addr, &swap32); + if (!ret) { + (*bytes_read) += 4; + if (userbuffer) { + if (put_user(swap32, + (u32 __user *)userbuffer)) + return -EFAULT; + + userbuffer += 4; + } else { + kernbuffer[0] = (swap32 >> 24) & 0xff; + kernbuffer[1] = (swap32 >> 16) & 0xff; + kernbuffer[2] = (swap32 >> 8) & 0xff; + kernbuffer[3] = swap32 & 0xff; + kernbuffer += 4; + } + addr += 4; + length -= 4; + } +#if 0 /* That does not work, as EP 2 is an OUT EP! */ + default: + CLEARPACKET(&packet); + packet.header = 0x001f; + packet.address = 0x000001a0; + packet.data = 0x00000006; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001b0; + packet.data = (length & ~3) | 0x40000000; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001b4; + packet.data = addr; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + packet.header = 0x001f; + packet.address = 0x000001a4; + packet.data = 0x00000001; + ret |= sisusb_send_bridge_packet(sisusb, 10, + &packet, 0); + if (userbuffer) { + ret |= sisusb_recv_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_IN, + (length & ~3), + NULL, userbuffer, + bytes_read, 0); + if (!ret) userbuffer += (*bytes_read); + } else { + ret |= sisusb_recv_bulk_msg(sisusb, + SISUSB_EP_GFX_BULK_IN, + (length & ~3), + kernbuffer, NULL, + bytes_read, 0); + if (!ret) kernbuffer += (*bytes_read); + } + addr += (*bytes_read); + length -= (*bytes_read); +#endif + } + + if (ret) + break; + } + + return ret; +} + +/* High level: Gfx (indexed) register access */ + +static int +sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data) +{ + int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); + return ret; +} + +static int +sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data) +{ + int ret; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, index); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, data); + return ret; +} + +static int +sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, + u8 myand, u8 myor) +{ + int ret; + u8 tmp; + + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); + tmp &= myand; + tmp |= myor; + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); + return ret; +} + +static int +sisusb_setidxregmask(struct sisusb_usb_data *sisusb, int port, u8 idx, + u8 data, u8 mask) +{ + int ret; + u8 tmp; + ret = sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, idx); + ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, &tmp); + tmp &= ~(mask); + tmp |= (data & mask); + ret |= sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port + 1, tmp); + return ret; +} + +static int +sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor) +{ + return(sisusb_setidxregandor(sisusb, port, index, 0xff, myor)); +} + +static int +sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand) +{ + return(sisusb_setidxregandor(sisusb, port, idx, myand, 0x00)); +} + +/* access pci config registers (reg numbers 0, 4, 8, etc) */ + +static int +sisusb_write_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = 0x008f; + packet.address = regnum | 0x10000; + packet.data = data; + ret = sisusb_send_packet(sisusb, 10, &packet); + return ret; +} + +static int +sisusb_read_pci_config(struct sisusb_usb_data *sisusb, int regnum, u32 *data) +{ + struct sisusb_packet packet; + int ret; + + packet.header = 0x008f; + packet.address = (u32)regnum | 0x10000; + ret = sisusb_send_packet(sisusb, 6, &packet); + *data = packet.data; + return ret; +} + +/* Clear video RAM */ + +static int +sisusb_clear_vram(struct sisusb_usb_data *sisusb, u32 address, int length) +{ + int ret, i, j; + + if (address < sisusb->vrambase) + return 1; + + if (address >= sisusb->vrambase + sisusb->vramsize) + return 1; + + if (address + length > sisusb->vrambase + sisusb->vramsize) + length = sisusb->vrambase + sisusb->vramsize - address; + + if (length <= 0) + return 0; + + /* allocate free buffer/urb and clear the buffer */ + if ((i = sisusb_alloc_outbuf(sisusb)) < 0) + return -EBUSY; + + memset(sisusb->obuf[i], 0, sisusb->obufsize); + + /* We can write a length > buffer size here. The buffer + * data will simply be re-used (like a ring-buffer). + */ + ret = sisusb_write_mem_bulk(sisusb, address, NULL, length, NULL, i, &j); + + /* Free the buffer/urb */ + sisusb_free_outbuf(sisusb, i); + + return ret; +} + +/* Initialize the graphics core (return 0 on success) + * This resets the graphics hardware and puts it into + * a defined mode (640x480@60Hz) + */ + +#define GETREG(r,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETREG(r,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, r, d) +#define SETIREG(r,i,d) sisusb_setidxreg(sisusb, r, i, d) +#define GETIREG(r,i,d) sisusb_getidxreg(sisusb, r, i, d) +#define SETIREGOR(r,i,o) sisusb_setidxregor(sisusb, r, i, o) +#define SETIREGAND(r,i,a) sisusb_setidxregand(sisusb, r, i, a) +#define SETIREGANDOR(r,i,a,o) sisusb_setidxregandor(sisusb, r, i, a, o) +#define READL(a,d) sisusb_read_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEL(a,d) sisusb_write_memio_long(sisusb, SISUSB_TYPE_MEM, a, d) +#define READB(a,d) sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) +#define WRITEB(a,d) sisusb_write_memio_byte(sisusb, SISUSB_TYPE_MEM, a, d) + +static int +sisusb_triggersr16(struct sisusb_usb_data *sisusb, u8 ramtype) +{ + int ret; + u8 tmp8; + + ret = GETIREG(SISSR, 0x16, &tmp8); + if (ramtype <= 1) { + tmp8 &= 0x3f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0x80; + ret |= SETIREG(SISSR, 0x16, tmp8); + } else { + tmp8 |= 0xc0; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0x80; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0xd0; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 &= 0x0f; + ret |= SETIREG(SISSR, 0x16, tmp8); + tmp8 |= 0xa0; + ret |= SETIREG(SISSR, 0x16, tmp8); + } + return ret; +} + +static int +sisusb_getbuswidth(struct sisusb_usb_data *sisusb, int *bw, int *chab) +{ + int ret; + u8 ramtype, done = 0; + u32 t0, t1, t2, t3; + u32 ramptr = SISUSB_PCI_MEMBASE; + + ret = GETIREG(SISSR, 0x3a, &ramtype); + ramtype &= 3; + + ret |= SETIREG(SISSR, 0x13, 0x00); + + if (ramtype <= 1) { + ret |= SETIREG(SISSR, 0x14, 0x12); + ret |= SETIREGAND(SISSR, 0x15, 0xef); + } else { + ret |= SETIREG(SISSR, 0x14, 0x02); + } + + ret |= sisusb_triggersr16(sisusb, ramtype); + ret |= WRITEL(ramptr + 0, 0x01234567); + ret |= WRITEL(ramptr + 4, 0x456789ab); + ret |= WRITEL(ramptr + 8, 0x89abcdef); + ret |= WRITEL(ramptr + 12, 0xcdef0123); + ret |= WRITEL(ramptr + 16, 0x55555555); + ret |= WRITEL(ramptr + 20, 0x55555555); + ret |= WRITEL(ramptr + 24, 0xffffffff); + ret |= WRITEL(ramptr + 28, 0xffffffff); + ret |= READL(ramptr + 0, &t0); + ret |= READL(ramptr + 4, &t1); + ret |= READL(ramptr + 8, &t2); + ret |= READL(ramptr + 12, &t3); + + if (ramtype <= 1) { + + *chab = 0; *bw = 64; + + if ((t3 != 0xcdef0123) || (t2 != 0x89abcdef)) { + if ((t1 == 0x456789ab) && (t0 == 0x01234567)) { + *chab = 0; *bw = 64; + ret |= SETIREGAND(SISSR, 0x14, 0xfd); + } + } + if ((t1 != 0x456789ab) || (t0 != 0x01234567)) { + *chab = 1; *bw = 64; + ret |= SETIREGANDOR(SISSR, 0x14, 0xfc,0x01); + + ret |= sisusb_triggersr16(sisusb, ramtype); + ret |= WRITEL(ramptr + 0, 0x89abcdef); + ret |= WRITEL(ramptr + 4, 0xcdef0123); + ret |= WRITEL(ramptr + 8, 0x55555555); + ret |= WRITEL(ramptr + 12, 0x55555555); + ret |= WRITEL(ramptr + 16, 0xaaaaaaaa); + ret |= WRITEL(ramptr + 20, 0xaaaaaaaa); + ret |= READL(ramptr + 4, &t1); + + if (t1 != 0xcdef0123) { + *bw = 32; + ret |= SETIREGOR(SISSR, 0x15, 0x10); + } + } + + } else { + + *chab = 0; *bw = 64; /* default: cha, bw = 64 */ + + done = 0; + + if (t1 == 0x456789ab) { + if (t0 == 0x01234567) { + *chab = 0; *bw = 64; + done = 1; + } + } else { + if (t0 == 0x01234567) { + *chab = 0; *bw = 32; + ret |= SETIREG(SISSR, 0x14, 0x00); + done = 1; + } + } + + if (!done) { + ret |= SETIREG(SISSR, 0x14, 0x03); + ret |= sisusb_triggersr16(sisusb, ramtype); + + ret |= WRITEL(ramptr + 0, 0x01234567); + ret |= WRITEL(ramptr + 4, 0x456789ab); + ret |= WRITEL(ramptr + 8, 0x89abcdef); + ret |= WRITEL(ramptr + 12, 0xcdef0123); + ret |= WRITEL(ramptr + 16, 0x55555555); + ret |= WRITEL(ramptr + 20, 0x55555555); + ret |= WRITEL(ramptr + 24, 0xffffffff); + ret |= WRITEL(ramptr + 28, 0xffffffff); + ret |= READL(ramptr + 0, &t0); + ret |= READL(ramptr + 4, &t1); + + if (t1 == 0x456789ab) { + if (t0 == 0x01234567) { + *chab = 1; *bw = 64; + return ret; + } /* else error */ + } else { + if (t0 == 0x01234567) { + *chab = 1; *bw = 32; + ret |= SETIREG(SISSR, 0x14, 0x01); + } /* else error */ + } + } + } + return ret; +} + +static int +sisusb_verify_mclk(struct sisusb_usb_data *sisusb) +{ + int ret = 0; + u32 ramptr = SISUSB_PCI_MEMBASE; + u8 tmp1, tmp2, i, j; + + ret |= WRITEB(ramptr, 0xaa); + ret |= WRITEB(ramptr + 16, 0x55); + ret |= READB(ramptr, &tmp1); + ret |= READB(ramptr + 16, &tmp2); + if ((tmp1 != 0xaa) || (tmp2 != 0x55)) { + for (i = 0, j = 16; i < 2; i++, j += 16) { + ret |= GETIREG(SISSR, 0x21, &tmp1); + ret |= SETIREGAND(SISSR, 0x21, (tmp1 & 0xfb)); + ret |= SETIREGOR(SISSR, 0x3c, 0x01); /* not on 330 */ + ret |= SETIREGAND(SISSR, 0x3c, 0xfe); /* not on 330 */ + ret |= SETIREG(SISSR, 0x21, tmp1); + ret |= WRITEB(ramptr + 16 + j, j); + ret |= READB(ramptr + 16 + j, &tmp1); + if (tmp1 == j) { + ret |= WRITEB(ramptr + j, j); + break; + } + } + } + return ret; +} + +static int +sisusb_set_rank(struct sisusb_usb_data *sisusb, int *iret, int index, + u8 rankno, u8 chab, const u8 dramtype[][5], + int bw) +{ + int ret = 0, ranksize; + u8 tmp; + + *iret = 0; + + if ((rankno == 2) && (dramtype[index][0] == 2)) + return ret; + + ranksize = dramtype[index][3] / 2 * bw / 32; + + if ((ranksize * rankno) > 128) + return ret; + + tmp = 0; + while ((ranksize >>= 1) > 0) tmp += 0x10; + tmp |= ((rankno - 1) << 2); + tmp |= ((bw / 64) & 0x02); + tmp |= (chab & 0x01); + + ret = SETIREG(SISSR, 0x14, tmp); + ret |= sisusb_triggersr16(sisusb, 0); /* sic! */ + + *iret = 1; + + return ret; +} + +static int +sisusb_check_rbc(struct sisusb_usb_data *sisusb, int *iret, u32 inc, int testn) +{ + int ret = 0, i; + u32 j, tmp; + + *iret = 0; + + for (i = 0, j = 0; i < testn; i++) { + ret |= WRITEL(sisusb->vrambase + j, j); + j += inc; + } + + for (i = 0, j = 0; i < testn; i++) { + ret |= READL(sisusb->vrambase + j, &tmp); + if (tmp != j) return ret; + j += inc; + } + + *iret = 1; + return ret; +} + +static int +sisusb_check_ranks(struct sisusb_usb_data *sisusb, int *iret, int rankno, + int idx, int bw, const u8 rtype[][5]) +{ + int ret = 0, i, i2ret; + u32 inc; + + *iret = 0; + + for (i = rankno; i >= 1; i--) { + inc = 1 << (rtype[idx][2] + + rtype[idx][1] + + rtype[idx][0] + + bw / 64 + i); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); + if (!i2ret) + return ret; + } + + inc = 1 << (rtype[idx][2] + bw / 64 + 2); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 4); + if (!i2ret) + return ret; + + inc = 1 << (10 + bw / 64); + ret |= sisusb_check_rbc(sisusb, &i2ret, inc, 2); + if (!i2ret) + return ret; + + *iret = 1; + return ret; +} + +static int +sisusb_get_sdram_size(struct sisusb_usb_data *sisusb, int *iret, int bw, + int chab) +{ + int ret = 0, i2ret = 0, i, j; + static const u8 sdramtype[13][5] = { + { 2, 12, 9, 64, 0x35 }, + { 1, 13, 9, 64, 0x44 }, + { 2, 12, 8, 32, 0x31 }, + { 2, 11, 9, 32, 0x25 }, + { 1, 12, 9, 32, 0x34 }, + { 1, 13, 8, 32, 0x40 }, + { 2, 11, 8, 16, 0x21 }, + { 1, 12, 8, 16, 0x30 }, + { 1, 11, 9, 16, 0x24 }, + { 1, 11, 8, 8, 0x20 }, + { 2, 9, 8, 4, 0x01 }, + { 1, 10, 8, 4, 0x10 }, + { 1, 9, 8, 2, 0x00 } + }; + + *iret = 1; /* error */ + + for (i = 0; i < 13; i++) { + ret |= SETIREGANDOR(SISSR, 0x13, 0x80, sdramtype[i][4]); + for (j = 2; j > 0; j--) { + ret |= sisusb_set_rank(sisusb, &i2ret, i, j, + chab, sdramtype, bw); + if (!i2ret) + continue; + + ret |= sisusb_check_ranks(sisusb, &i2ret, j, i, + bw, sdramtype); + if (i2ret) { + *iret = 0; /* ram size found */ + return ret; + } + } + } + + return ret; +} + +static int +sisusb_setup_screen(struct sisusb_usb_data *sisusb, int clrall, int drwfr) +{ + int ret = 0; + u32 address; + int i, length, modex, modey, bpp; + + modex = 640; modey = 480; bpp = 2; + + address = sisusb->vrambase; /* Clear video ram */ + + if (clrall) + length = sisusb->vramsize; + else + length = modex * bpp * modey; + + ret = sisusb_clear_vram(sisusb, address, length); + + if (!ret && drwfr) { + for (i = 0; i < modex; i++) { + address = sisusb->vrambase + (i * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + address += (modex * (modey-1) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + } + for (i = 0; i < modey; i++) { + address = sisusb->vrambase + ((i * modex) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + address += ((modex - 1) * bpp); + ret |= sisusb_write_memio_word(sisusb, SISUSB_TYPE_MEM, + address, 0xf100); + } + } + + return ret; +} + +static int +sisusb_set_default_mode(struct sisusb_usb_data *sisusb, int touchengines) +{ + int ret = 0, i, j, modex, modey, bpp, du; + u8 sr31, cr63, tmp8; + static const char attrdata[] = { + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x01,0x00,0x00,0x00 + }; + static const char crtcrdata[] = { + 0x5f,0x4f,0x50,0x82,0x54,0x80,0x0b,0x3e, + 0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00, + 0xea,0x8c,0xdf,0x28,0x40,0xe7,0x04,0xa3, + 0xff + }; + static const char grcdata[] = { + 0x00,0x00,0x00,0x00,0x00,0x40,0x05,0x0f, + 0xff + }; + static const char crtcdata[] = { + 0x5f,0x4f,0x4f,0x83,0x55,0x81,0x0b,0x3e, + 0xe9,0x8b,0xdf,0xe8,0x0c,0x00,0x00,0x05, + 0x00 + }; + + modex = 640; modey = 480; bpp = 2; + + GETIREG(SISSR, 0x31, &sr31); + GETIREG(SISCR, 0x63, &cr63); + SETIREGOR(SISSR, 0x01, 0x20); + SETIREG(SISCR, 0x63, cr63 & 0xbf); + SETIREGOR(SISCR, 0x17, 0x80); + SETIREGOR(SISSR, 0x1f, 0x04); + SETIREGAND(SISSR, 0x07, 0xfb); + SETIREG(SISSR, 0x00, 0x03); /* seq */ + SETIREG(SISSR, 0x01, 0x21); + SETIREG(SISSR, 0x02, 0x0f); + SETIREG(SISSR, 0x03, 0x00); + SETIREG(SISSR, 0x04, 0x0e); + SETREG(SISMISCW, 0x23); /* misc */ + for (i = 0; i <= 0x18; i++) { /* crtc */ + SETIREG(SISCR, i, crtcrdata[i]); + } + for (i = 0; i <= 0x13; i++) { /* att */ + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, i); + SETREG(SISAR, attrdata[i]); + } + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, 0x14); + SETREG(SISAR, 0x00); + GETREG(SISINPSTAT, &tmp8); + SETREG(SISAR, 0x20); + GETREG(SISINPSTAT, &tmp8); + for (i = 0; i <= 0x08; i++) { /* grc */ + SETIREG(SISGR, i, grcdata[i]); + } + SETIREGAND(SISGR, 0x05, 0xbf); + for (i = 0x0A; i <= 0x0E; i++) { /* clr ext */ + SETIREG(SISSR, i, 0x00); + } + SETIREGAND(SISSR, 0x37, 0xfe); + SETREG(SISMISCW, 0xef); /* sync */ + SETIREG(SISCR, 0x11, 0x00); /* crtc */ + for (j = 0x00, i = 0; i <= 7; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x10; i <= 10; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x15; i <= 12; i++, j++) { + SETIREG(SISCR, j, crtcdata[i]); + } + for (j = 0x0A; i <= 15; i++, j++) { + SETIREG(SISSR, j, crtcdata[i]); + } + SETIREG(SISSR, 0x0E, (crtcdata[16] & 0xE0)); + SETIREGANDOR(SISCR, 0x09, 0x5f, ((crtcdata[16] & 0x01) << 5)); + SETIREG(SISCR, 0x14, 0x4f); + du = (modex / 16) * (bpp * 2); /* offset/pitch */ + if (modex % 16) du += bpp; + SETIREGANDOR(SISSR, 0x0e, 0xf0, ((du >> 8) & 0x0f)); + SETIREG(SISCR, 0x13, (du & 0xff)); + du <<= 5; + tmp8 = du >> 8; + if (du & 0xff) tmp8++; + SETIREG(SISSR, 0x10, tmp8); + SETIREG(SISSR, 0x31, 0x00); /* VCLK */ + SETIREG(SISSR, 0x2b, 0x1b); + SETIREG(SISSR, 0x2c, 0xe1); + SETIREG(SISSR, 0x2d, 0x01); + SETIREGAND(SISSR, 0x3d, 0xfe); /* FIFO */ + SETIREG(SISSR, 0x08, 0xae); + SETIREGAND(SISSR, 0x09, 0xf0); + SETIREG(SISSR, 0x08, 0x34); + SETIREGOR(SISSR, 0x3d, 0x01); + SETIREGAND(SISSR, 0x1f, 0x3f); /* mode regs */ + SETIREGANDOR(SISSR, 0x06, 0xc0, 0x0a); + SETIREG(SISCR, 0x19, 0x00); + SETIREGAND(SISCR, 0x1a, 0xfc); + SETIREGAND(SISSR, 0x0f, 0xb7); + SETIREGAND(SISSR, 0x31, 0xfb); + SETIREGANDOR(SISSR, 0x21, 0x1f, 0xa0); + SETIREGAND(SISSR, 0x32, 0xf3); + SETIREGANDOR(SISSR, 0x07, 0xf8, 0x03); + SETIREG(SISCR, 0x52, 0x6c); + + SETIREG(SISCR, 0x0d, 0x00); /* adjust frame */ + SETIREG(SISCR, 0x0c, 0x00); + SETIREG(SISSR, 0x0d, 0x00); + SETIREGAND(SISSR, 0x37, 0xfe); + + SETIREG(SISCR, 0x32, 0x20); + SETIREGAND(SISSR, 0x01, 0xdf); /* enable display */ + SETIREG(SISCR, 0x63, (cr63 & 0xbf)); + SETIREG(SISSR, 0x31, (sr31 & 0xfb)); + + if (touchengines) { + SETIREG(SISSR, 0x20, 0xa1); /* enable engines */ + SETIREGOR(SISSR, 0x1e, 0x5a); + + SETIREG(SISSR, 0x26, 0x01); /* disable cmdqueue */ + SETIREG(SISSR, 0x27, 0x1f); + SETIREG(SISSR, 0x26, 0x00); + } + + SETIREG(SISCR, 0x34, 0x44); /* we just set std mode #44 */ + + return ret; +} + +static int +sisusb_init_gfxcore(struct sisusb_usb_data *sisusb) +{ + int ret = 0, i, j, bw, chab, iret, retry = 3; + u8 tmp8, ramtype; + u32 tmp32; + static const char mclktable[] = { + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143 + }; + static const char eclktable[] = { + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143, + 0x3b, 0x22, 0x01, 143 + }; + static const char ramtypetable1[] = { + 0x00, 0x04, 0x60, 0x60, + 0x0f, 0x0f, 0x1f, 0x1f, + 0xba, 0xba, 0xba, 0xba, + 0xa9, 0xa9, 0xac, 0xac, + 0xa0, 0xa0, 0xa0, 0xa8, + 0x00, 0x00, 0x02, 0x02, + 0x30, 0x30, 0x40, 0x40 + }; + static const char ramtypetable2[] = { + 0x77, 0x77, 0x44, 0x44, + 0x77, 0x77, 0x44, 0x44, + 0x00, 0x00, 0x00, 0x00, + 0x5b, 0x5b, 0xab, 0xab, + 0x00, 0x00, 0xf0, 0xf8 + }; + + while (retry--) { + + /* Enable VGA */ + ret = GETREG(SISVGAEN, &tmp8); + ret |= SETREG(SISVGAEN, (tmp8 | 0x01)); + + /* Enable GPU access to VRAM */ + ret |= GETREG(SISMISCR, &tmp8); + ret |= SETREG(SISMISCW, (tmp8 | 0x01)); + + if (ret) continue; + + /* Reset registers */ + ret |= SETIREGAND(SISCR, 0x5b, 0xdf); + ret |= SETIREG(SISSR, 0x05, 0x86); + ret |= SETIREGOR(SISSR, 0x20, 0x01); + + ret |= SETREG(SISMISCW, 0x67); + + for (i = 0x06; i <= 0x1f; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x21; i <= 0x27; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x31; i <= 0x3d; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x12; i <= 0x1b; i++) { + ret |= SETIREG(SISSR, i, 0x00); + } + for (i = 0x79; i <= 0x7c; i++) { + ret |= SETIREG(SISCR, i, 0x00); + } + + if (ret) continue; + + ret |= SETIREG(SISCR, 0x63, 0x80); + + ret |= GETIREG(SISSR, 0x3a, &ramtype); + ramtype &= 0x03; + + ret |= SETIREG(SISSR, 0x28, mclktable[ramtype * 4]); + ret |= SETIREG(SISSR, 0x29, mclktable[(ramtype * 4) + 1]); + ret |= SETIREG(SISSR, 0x2a, mclktable[(ramtype * 4) + 2]); + + ret |= SETIREG(SISSR, 0x2e, eclktable[ramtype * 4]); + ret |= SETIREG(SISSR, 0x2f, eclktable[(ramtype * 4) + 1]); + ret |= SETIREG(SISSR, 0x30, eclktable[(ramtype * 4) + 2]); + + ret |= SETIREG(SISSR, 0x07, 0x18); + ret |= SETIREG(SISSR, 0x11, 0x0f); + + if (ret) continue; + + for (i = 0x15, j = 0; i <= 0x1b; i++, j++) { + ret |= SETIREG(SISSR, i, ramtypetable1[(j*4) + ramtype]); + } + for (i = 0x40, j = 0; i <= 0x44; i++, j++) { + ret |= SETIREG(SISCR, i, ramtypetable2[(j*4) + ramtype]); + } + + ret |= SETIREG(SISCR, 0x49, 0xaa); + + ret |= SETIREG(SISSR, 0x1f, 0x00); + ret |= SETIREG(SISSR, 0x20, 0xa0); + ret |= SETIREG(SISSR, 0x23, 0xf6); + ret |= SETIREG(SISSR, 0x24, 0x0d); + ret |= SETIREG(SISSR, 0x25, 0x33); + + ret |= SETIREG(SISSR, 0x11, 0x0f); + + ret |= SETIREGOR(SISPART1, 0x2f, 0x01); + + ret |= SETIREGAND(SISCAP, 0x3f, 0xef); + + if (ret) continue; + + ret |= SETIREG(SISPART1, 0x00, 0x00); + + ret |= GETIREG(SISSR, 0x13, &tmp8); + tmp8 >>= 4; + + ret |= SETIREG(SISPART1, 0x02, 0x00); + ret |= SETIREG(SISPART1, 0x2e, 0x08); + + ret |= sisusb_read_pci_config(sisusb, 0x50, &tmp32); + tmp32 &= 0x00f00000; + tmp8 = (tmp32 == 0x100000) ? 0x33 : 0x03; + ret |= SETIREG(SISSR, 0x25, tmp8); + tmp8 = (tmp32 == 0x100000) ? 0xaa : 0x88; + ret |= SETIREG(SISCR, 0x49, tmp8); + + ret |= SETIREG(SISSR, 0x27, 0x1f); + ret |= SETIREG(SISSR, 0x31, 0x00); + ret |= SETIREG(SISSR, 0x32, 0x11); + ret |= SETIREG(SISSR, 0x33, 0x00); + + if (ret) continue; + + ret |= SETIREG(SISCR, 0x83, 0x00); + + ret |= sisusb_set_default_mode(sisusb, 0); + + ret |= SETIREGAND(SISSR, 0x21, 0xdf); + ret |= SETIREGOR(SISSR, 0x01, 0x20); + ret |= SETIREGOR(SISSR, 0x16, 0x0f); + + ret |= sisusb_triggersr16(sisusb, ramtype); + + /* Disable refresh */ + ret |= SETIREGAND(SISSR, 0x17, 0xf8); + ret |= SETIREGOR(SISSR, 0x19, 0x03); + + ret |= sisusb_getbuswidth(sisusb, &bw, &chab); + ret |= sisusb_verify_mclk(sisusb); + + if (ramtype <= 1) { + ret |= sisusb_get_sdram_size(sisusb, &iret, bw, chab); + if (iret) { + printk(KERN_ERR "sisusbvga[%d]: RAM size " + "detection failed, " + "assuming 8MB video RAM\n", + sisusb->minor); + ret |= SETIREG(SISSR,0x14,0x31); + /* TODO */ + } + } else { + printk(KERN_ERR "sisusbvga[%d]: DDR RAM device found, " + "assuming 8MB video RAM\n", + sisusb->minor); + ret |= SETIREG(SISSR,0x14,0x31); + /* *** TODO *** */ + } + + /* Enable refresh */ + ret |= SETIREG(SISSR, 0x16, ramtypetable1[4 + ramtype]); + ret |= SETIREG(SISSR, 0x17, ramtypetable1[8 + ramtype]); + ret |= SETIREG(SISSR, 0x19, ramtypetable1[16 + ramtype]); + + ret |= SETIREGOR(SISSR, 0x21, 0x20); + + ret |= SETIREG(SISSR, 0x22, 0xfb); + ret |= SETIREG(SISSR, 0x21, 0xa5); + + if (ret == 0) + break; + } + + return ret; +} + +#undef SETREG +#undef GETREG +#undef SETIREG +#undef GETIREG +#undef SETIREGOR +#undef SETIREGAND +#undef SETIREGANDOR +#undef READL +#undef WRITEL + +static void +sisusb_get_ramconfig(struct sisusb_usb_data *sisusb) +{ + u8 tmp8, tmp82, ramtype; + int bw = 0; + char *ramtypetext1 = NULL; + const char *ramtypetext2[] = { "SDR SDRAM", "SDR SGRAM", + "DDR SDRAM", "DDR SGRAM" }; + static const int busSDR[4] = {64, 64, 128, 128}; + static const int busDDR[4] = {32, 32, 64, 64}; + static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2}; + + sisusb_getidxreg(sisusb, SISSR, 0x14, &tmp8); + sisusb_getidxreg(sisusb, SISSR, 0x15, &tmp82); + sisusb_getidxreg(sisusb, SISSR, 0x3a, &ramtype); + sisusb->vramsize = (1 << ((tmp8 & 0xf0) >> 4)) * 1024 * 1024; + ramtype &= 0x03; + switch ((tmp8 >> 2) & 0x03) { + case 0: ramtypetext1 = "1 ch/1 r"; + if (tmp82 & 0x10) { + bw = 32; + } else { + bw = busSDR[(tmp8 & 0x03)]; + } + break; + case 1: ramtypetext1 = "1 ch/2 r"; + sisusb->vramsize <<= 1; + bw = busSDR[(tmp8 & 0x03)]; + break; + case 2: ramtypetext1 = "asymmeric"; + sisusb->vramsize += sisusb->vramsize/2; + bw = busDDRA[(tmp8 & 0x03)]; + break; + case 3: ramtypetext1 = "2 channel"; + sisusb->vramsize <<= 1; + bw = busDDR[(tmp8 & 0x03)]; + break; + } + + printk(KERN_INFO "sisusbvga[%d]: %dMB %s %s, bus width %d\n", + sisusb->minor, (sisusb->vramsize >> 20), ramtypetext1, + ramtypetext2[ramtype], bw); +} + +static int +sisusb_do_init_gfxdevice(struct sisusb_usb_data *sisusb) +{ + struct sisusb_packet packet; + int ret; + u32 tmp32; + + /* Do some magic */ + packet.header = 0x001f; + packet.address = 0x00000324; + packet.data = 0x00000004; + ret = sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000364; + packet.data = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000384; + packet.data = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x001f; + packet.address = 0x00000100; + packet.data = 0x00000700; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + packet.header = 0x000f; + packet.address = 0x00000004; + ret |= sisusb_send_bridge_packet(sisusb, 6, &packet, 0); + packet.data |= 0x17; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + + /* Init BAR 0 (VRAM) */ + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x10, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_MEMBASE; + ret |= sisusb_write_pci_config(sisusb, 0x10, tmp32); + + /* Init BAR 1 (MMIO) */ + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x14, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_MMIOBASE; + ret |= sisusb_write_pci_config(sisusb, 0x14, tmp32); + + /* Init BAR 2 (i/o ports) */ + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + ret |= sisusb_write_pci_config(sisusb, 0x18, 0xfffffff0); + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + tmp32 &= 0x0f; + tmp32 |= SISUSB_PCI_IOPORTBASE; + ret |= sisusb_write_pci_config(sisusb, 0x18, tmp32); + + /* Enable memory and i/o access */ + ret |= sisusb_read_pci_config(sisusb, 0x04, &tmp32); + tmp32 |= 0x3; + ret |= sisusb_write_pci_config(sisusb, 0x04, tmp32); + + if (ret == 0) { + /* Some further magic */ + packet.header = 0x001f; + packet.address = 0x00000050; + packet.data = 0x000000ff; + ret |= sisusb_send_bridge_packet(sisusb, 10, &packet, 0); + } + + return ret; +} + +/* Initialize the graphics device (return 0 on success) + * This initializes the net2280 as well as the PCI registers + * of the graphics board. + */ + +static int +sisusb_init_gfxdevice(struct sisusb_usb_data *sisusb, int initscreen) +{ + int ret = 0, test = 0; + u32 tmp32; + + if (sisusb->devinit == 1) { + /* Read PCI BARs and see if they have been set up */ + ret |= sisusb_read_pci_config(sisusb, 0x10, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MEMBASE) test++; + + ret |= sisusb_read_pci_config(sisusb, 0x14, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_MMIOBASE) test++; + + ret |= sisusb_read_pci_config(sisusb, 0x18, &tmp32); + if (ret) return ret; + if ((tmp32 & 0xfffffff0) == SISUSB_PCI_IOPORTBASE) test++; + } + + /* No? So reset the device */ + if ((sisusb->devinit == 0) || (test != 3)) { + + ret |= sisusb_do_init_gfxdevice(sisusb); + + if (ret == 0) + sisusb->devinit = 1; + + } + + if (sisusb->devinit) { + /* Initialize the graphics core */ + if (sisusb_init_gfxcore(sisusb) == 0) { + sisusb->gfxinit = 1; + sisusb_get_ramconfig(sisusb); + ret |= sisusb_set_default_mode(sisusb, 1); + ret |= sisusb_setup_screen(sisusb, 1, initscreen); + } + } + + return ret; +} + +/* fops */ + +int +sisusb_open(struct inode *inode, struct file *file) +{ + struct sisusb_usb_data *sisusb; + struct usb_interface *interface; + int subminor = iminor(inode); + + down(&disconnect_sem); + + if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { + printk(KERN_ERR "sisusb[%d]: Failed to find interface\n", + subminor); + up(&disconnect_sem); + return -ENODEV; + } + + if (!(sisusb = usb_get_intfdata(interface))) { + up(&disconnect_sem); + return -ENODEV; + } + + down(&sisusb->lock); + + if (!sisusb->present || !sisusb->ready) { + up(&sisusb->lock); + up(&disconnect_sem); + return -ENODEV; + } + + if (sisusb->isopen) { + up(&sisusb->lock); + up(&disconnect_sem); + return -EBUSY; + } + + if (!sisusb->devinit) { + if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) { + if (sisusb_init_gfxdevice(sisusb, 0)) { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Failed to initialize " + "device\n", + sisusb->minor); + return -EIO; + } + } else { + up(&sisusb->lock); + up(&disconnect_sem); + printk(KERN_ERR + "sisusbvga[%d]: Device not attached to " + "USB 2.0 hub\n", + sisusb->minor); + return -EIO; + } + } + + /* increment usage count for the device */ + kref_get(&sisusb->kref); + + sisusb->isopen = 1; + + file->private_data = sisusb; + + up(&sisusb->lock); + + up(&disconnect_sem); + + printk(KERN_DEBUG "sisusbvga[%d]: opened", sisusb->minor); + + return 0; +} + +static void +sisusb_delete(struct kref *kref) +{ + struct sisusb_usb_data *sisusb = to_sisusb_dev(kref); + + if (!sisusb) + return; + + if (sisusb->sisusb_dev) + usb_put_dev(sisusb->sisusb_dev); + + sisusb->sisusb_dev = NULL; + sisusb_free_buffers(sisusb); + sisusb_free_urbs(sisusb); + kfree(sisusb); +} + +int +sisusb_release(struct inode *inode, struct file *file) +{ + struct sisusb_usb_data *sisusb; + int myminor; + + down(&disconnect_sem); + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) { + up(&disconnect_sem); + return -ENODEV; + } + + down(&sisusb->lock); + + if (sisusb->present) { + /* Wait for all URBs to finish if device still present */ + if (!sisusb_wait_all_out_complete(sisusb)) + sisusb_kill_all_busy(sisusb); + } + + myminor = sisusb->minor; + + sisusb->isopen = 0; + file->private_data = NULL; + + up(&sisusb->lock); + + /* decrement the usage count on our device */ + kref_put(&sisusb->kref, sisusb_delete); + + up(&disconnect_sem); + + printk(KERN_DEBUG "sisusbvga[%d]: released", myminor); + + return 0; +} + +ssize_t +sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct sisusb_usb_data *sisusb; + ssize_t bytes_read = 0; + int errno = 0; + u8 buf8; + u16 buf16; + u32 buf32, address; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + /* Read i/o ports + * Byte, word and long(32) can be read. As this + * emulates inX instructions, the data returned is + * in machine-endianness. + */ + switch (count) { + + case 1: + if (sisusb_read_memio_byte(sisusb, + SISUSB_TYPE_IO, + address, &buf8)) + errno = -EIO; + else if (put_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 1; + + break; + + case 2: + if (sisusb_read_memio_word(sisusb, + SISUSB_TYPE_IO, + address, &buf16)) + errno = -EIO; + else if (put_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 2; + + break; + + case 4: + if (sisusb_read_memio_long(sisusb, + SISUSB_TYPE_IO, + address, &buf32)) + errno = -EIO; + else if (put_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 4; + + break; + + default: + errno = -EIO; + + } + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + + /* Read video ram + * Remember: Data delivered is never endian-corrected + */ + errno = sisusb_read_mem_bulk(sisusb, address, + NULL, count, buffer, &bytes_read); + + if (bytes_read) + errno = bytes_read; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; + + /* Read MMIO + * Remember: Data delivered is never endian-corrected + */ + errno = sisusb_read_mem_bulk(sisusb, address, + NULL, count, buffer, &bytes_read); + + if (bytes_read) + errno = bytes_read; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + 0x5c) { + + if (count != 4) { + up(&sisusb->lock); + return -EINVAL; + } + + address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; + + /* Read PCI config register + * Return value delivered in machine endianness. + */ + if (sisusb_read_pci_config(sisusb, address, &buf32)) + errno = -EIO; + else if (put_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else + bytes_read = 4; + + } else { + + errno = -EBADFD; + + } + + (*ppos) += bytes_read; + + up(&sisusb->lock); + + return errno ? errno : bytes_read; +} + +ssize_t +sisusb_write(struct file *file, const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct sisusb_usb_data *sisusb; + int errno = 0; + ssize_t bytes_written = 0; + u8 buf8; + u16 buf16; + u32 buf32, address; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + if ((*ppos) >= SISUSB_PCI_PSEUDO_IOPORTBASE && + (*ppos) < SISUSB_PCI_PSEUDO_IOPORTBASE + 128) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + /* Write i/o ports + * Byte, word and long(32) can be written. As this + * emulates outX instructions, the data is expected + * in machine-endianness. + */ + switch (count) { + + case 1: + if (get_user(buf8, (u8 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_byte(sisusb, + SISUSB_TYPE_IO, + address, buf8)) + errno = -EIO; + else + bytes_written = 1; + + break; + + case 2: + if (get_user(buf16, (u16 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_word(sisusb, + SISUSB_TYPE_IO, + address, buf16)) + errno = -EIO; + else + bytes_written = 2; + + break; + + case 4: + if (get_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_memio_long(sisusb, + SISUSB_TYPE_IO, + address, buf32)) + errno = -EIO; + else + bytes_written = 4; + + break; + + default: + errno = -EIO; + } + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MEMBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MEMBASE + sisusb->vramsize) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + + /* Write video ram. + * Buffer is copied 1:1, therefore, on big-endian + * machines, the data must be swapped by userland + * in advance (if applicable; no swapping in 8bpp + * mode or if YUV data is being transferred). + */ + errno = sisusb_write_mem_bulk(sisusb, address, NULL, + count, buffer, 0, &bytes_written); + + if (bytes_written) + errno = bytes_written; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_MMIOBASE && + (*ppos) < SISUSB_PCI_PSEUDO_MMIOBASE + SISUSB_PCI_MMIOSIZE) { + + address = (*ppos) - + SISUSB_PCI_PSEUDO_MMIOBASE + + SISUSB_PCI_MMIOBASE; + + /* Write MMIO. + * Buffer is copied 1:1, therefore, on big-endian + * machines, the data must be swapped by userland + * in advance. + */ + errno = sisusb_write_mem_bulk(sisusb, address, NULL, + count, buffer, 0, &bytes_written); + + if (bytes_written) + errno = bytes_written; + + } else if ((*ppos) >= SISUSB_PCI_PSEUDO_PCIBASE && + (*ppos) <= SISUSB_PCI_PSEUDO_PCIBASE + SISUSB_PCI_PCONFSIZE) { + + if (count != 4) { + up(&sisusb->lock); + return -EINVAL; + } + + address = (*ppos) - SISUSB_PCI_PSEUDO_PCIBASE; + + /* Write PCI config register. + * Given value expected in machine endianness. + */ + if (get_user(buf32, (u32 __user *)buffer)) + errno = -EFAULT; + else if (sisusb_write_pci_config(sisusb, address, buf32)) + errno = -EIO; + else + bytes_written = 4; + + + } else { + + /* Error */ + errno = -EBADFD; + + } + + (*ppos) += bytes_written; + + up(&sisusb->lock); + + return errno ? errno : bytes_written; +} + +static loff_t +sisusb_lseek(struct file *file, loff_t offset, int orig) +{ + struct sisusb_usb_data *sisusb; + loff_t ret; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + up(&sisusb->lock); + return -ENODEV; + } + + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + /* never negative, no force_successful_syscall needed */ + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + /* never negative, no force_successful_syscall needed */ + break; + default: + /* seeking relative to "end of file" is not supported */ + ret = -EINVAL; + } + + up(&sisusb->lock); + return ret; +} + +static int +sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y, + unsigned long arg) +{ + int retval, port, length; + u32 address; + + port = y->data3 - + SISUSB_PCI_PSEUDO_IOPORTBASE + + SISUSB_PCI_IOPORTBASE; + + switch (y->operation) { + case SUCMD_GET: + retval = sisusb_getidxreg(sisusb, port, + y->data0, &y->data1); + if (!retval) { + if (copy_to_user((void __user *)arg, y, + sizeof(*y))) + retval = -EFAULT; + } + break; + + case SUCMD_SET: + retval = sisusb_setidxreg(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETOR: + retval = sisusb_setidxregor(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETAND: + retval = sisusb_setidxregand(sisusb, port, + y->data0, y->data1); + break; + + case SUCMD_SETANDOR: + retval = sisusb_setidxregandor(sisusb, port, + y->data0, y->data1, y->data2); + break; + + case SUCMD_SETMASK: + retval = sisusb_setidxregmask(sisusb, port, + y->data0, y->data1, y->data2); + break; + + case SUCMD_CLRSCR: + length = (y->data0 << 16) | (y->data1 << 8) | y->data2; + address = y->data3 - + SISUSB_PCI_PSEUDO_MEMBASE + + SISUSB_PCI_MEMBASE; + retval = sisusb_clear_vram(sisusb, address, length); + break; + + default: + retval = -EINVAL; + } + + if(retval > 0) + retval = -EIO; + + return retval; +} + +static int +sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sisusb_usb_data *sisusb; + struct sisusb_info x; + struct sisusb_command y; + int retval = 0; + u32 __user *argp = (u32 __user *)arg; + + if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) + return -ENODEV; + + down(&sisusb->lock); + + /* Sanity check */ + if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) { + retval = -ENODEV; + goto err_out; + } + + switch (cmd) { + + case SISUSB_GET_CONFIG_SIZE: + + if (put_user(sizeof(x), argp)) + retval = -EFAULT; + + break; + + case SISUSB_GET_CONFIG: + + x.sisusb_id = SISUSB_ID; + x.sisusb_version = SISUSB_VERSION; + x.sisusb_revision = SISUSB_REVISION; + x.sisusb_patchlevel = SISUSB_PATCHLEVEL; + x.sisusb_gfxinit = sisusb->gfxinit; + x.sisusb_vrambase = SISUSB_PCI_PSEUDO_MEMBASE; + x.sisusb_mmiobase = SISUSB_PCI_PSEUDO_MMIOBASE; + x.sisusb_iobase = SISUSB_PCI_PSEUDO_IOPORTBASE; + x.sisusb_pcibase = SISUSB_PCI_PSEUDO_PCIBASE; + x.sisusb_vramsize = sisusb->vramsize; + x.sisusb_minor = sisusb->minor; + x.sisusb_fbdevactive= 0; + + if (copy_to_user((void __user *)arg, &x, sizeof(x))) + retval = -EFAULT; + + break; + + case SISUSB_COMMAND: + + if (copy_from_user(&y, (void __user *)arg, sizeof(y))) + retval = -EFAULT; + else + retval = sisusb_handle_command(sisusb, &y, arg); + + break; + + default: + retval = -EINVAL; + break; + } + +err_out: + up(&sisusb->lock); + return retval; +} + +#ifdef SISUSB_NEW_CONFIG_COMPAT +static long +sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + long retval; + + switch (cmd) { + case SISUSB_GET_CONFIG_SIZE: + case SISUSB_GET_CONFIG: + case SISUSB_COMMAND: + lock_kernel(); + retval = sisusb_ioctl(f->f_dentry->d_inode, f, cmd, arg); + unlock_kernel(); + return retval; + + default: + return -ENOIOCTLCMD; + } +} +#endif + +static struct file_operations usb_sisusb_fops = { + .owner = THIS_MODULE, + .open = sisusb_open, + .release = sisusb_release, + .read = sisusb_read, + .write = sisusb_write, + .llseek = sisusb_lseek, +#ifdef SISUSB_NEW_CONFIG_COMPAT + .compat_ioctl = sisusb_compat_ioctl, +#endif + .ioctl = sisusb_ioctl +}; + +static struct usb_class_driver usb_sisusb_class = { + .name = "usb/sisusbvga%d", + .fops = &usb_sisusb_fops, + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, + .minor_base = SISUSB_MINOR +}; + +static int sisusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct sisusb_usb_data *sisusb; + int retval = 0, i; + const char *memfail = + KERN_ERR + "sisusbvga[%d]: Failed to allocate memory for %s buffer\n"; + + printk(KERN_INFO "sisusb: USB2VGA dongle found at address %d\n", + dev->devnum); + + /* Allocate memory for our private */ + if (!(sisusb = kmalloc(sizeof(*sisusb), GFP_KERNEL))) { + printk(KERN_ERR + "sisusb: Failed to allocate memory for private data\n"); + return -ENOMEM; + } + memset(sisusb, 0, sizeof(*sisusb)); + kref_init(&sisusb->kref); + + init_MUTEX(&(sisusb->lock)); + + /* Register device */ + if ((retval = usb_register_dev(intf, &usb_sisusb_class))) { + printk(KERN_ERR + "sisusb: Failed to get a minor for device %d\n", + dev->devnum); + retval = -ENODEV; + goto error_1; + } + + sisusb->sisusb_dev = dev; + sisusb->minor = intf->minor; + sisusb->vrambase = SISUSB_PCI_MEMBASE; + sisusb->mmiobase = SISUSB_PCI_MMIOBASE; + sisusb->mmiosize = SISUSB_PCI_MMIOSIZE; + sisusb->ioportbase = SISUSB_PCI_IOPORTBASE; + /* Everything else is zero */ + + /* Allocate buffers */ + sisusb->ibufsize = SISUSB_IBUF_SIZE; + if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE, + GFP_KERNEL, &sisusb->transfer_dma_in))) { + printk(memfail, "input", sisusb->minor); + retval = -ENOMEM; + goto error_2; + } + + sisusb->numobufs = 0; + sisusb->obufsize = SISUSB_OBUF_SIZE; + for (i = 0; i < NUMOBUFS; i++) { + if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE, + GFP_KERNEL, + &sisusb->transfer_dma_out[i]))) { + if (i == 0) { + printk(memfail, "output", sisusb->minor); + retval = -ENOMEM; + goto error_3; + } + break; + } else + sisusb->numobufs++; + + } + + /* Allocate URBs */ + if (!(sisusb->sisurbin = usb_alloc_urb(0, GFP_KERNEL))) { + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate URBs\n", + sisusb->minor); + retval = -ENOMEM; + goto error_3; + } + sisusb->completein = 1; + + for (i = 0; i < sisusb->numobufs; i++) { + if (!(sisusb->sisurbout[i] = usb_alloc_urb(0, GFP_KERNEL))) { + printk(KERN_ERR + "sisusbvga[%d]: Failed to allocate URBs\n", + sisusb->minor); + retval = -ENOMEM; + goto error_4; + } + sisusb->urbout_context[i].sisusb = (void *)sisusb; + sisusb->urbout_context[i].urbindex = i; + sisusb->urbstatus[i] = 0; + } + + printk(KERN_INFO "sisusbvga[%d]: Allocated %d output buffers\n", + sisusb->minor, sisusb->numobufs); + + /* Do remaining init stuff */ + + init_waitqueue_head(&sisusb->wait_q); + + usb_set_intfdata(intf, sisusb); + +#ifdef SISUSB_OLD_CONFIG_COMPAT + { + int ret; + /* Our ioctls are all "32/64bit compatible" */ + ret = register_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE, NULL); + ret |= register_ioctl32_conversion(SISUSB_GET_CONFIG, NULL); + ret |= register_ioctl32_conversion(SISUSB_COMMAND, NULL); + if (ret) + printk(KERN_ERR + "sisusbvga[%d]: Error registering ioctl32 " + "translations\n", + sisusb->minor); + else + sisusb->ioctl32registered = 1; + + } +#endif + + sisusb->present = 1; + + if (dev->speed == USB_SPEED_HIGH) { + if (sisusb_init_gfxdevice(sisusb, 1)) + printk(KERN_ERR + "sisusbvga[%d]: Failed to early " + "initialize device\n", + sisusb->minor); + + } else + printk(KERN_INFO + "sisusbvga[%d]: Not attached to USB 2.0 hub, " + "deferring init\n", + sisusb->minor); + + sisusb->ready = 1; + + return 0; + +error_4: + sisusb_free_urbs(sisusb); +error_3: + sisusb_free_buffers(sisusb); +error_2: + usb_deregister_dev(intf, &usb_sisusb_class); +error_1: + kfree(sisusb); + return retval; +} + +static void sisusb_disconnect(struct usb_interface *intf) +{ + struct sisusb_usb_data *sisusb; + int minor; + + down(&disconnect_sem); + + /* This should *not* happen */ + if (!(sisusb = usb_get_intfdata(intf))) { + up(&disconnect_sem); + return; + } + + down(&sisusb->lock); + + /* Wait for all URBs to complete and kill them in case (MUST do) */ + if (!sisusb_wait_all_out_complete(sisusb)) + sisusb_kill_all_busy(sisusb); + + minor = sisusb->minor; + + usb_set_intfdata(intf, NULL); + + usb_deregister_dev(intf, &usb_sisusb_class); + +#ifdef SISUSB_OLD_CONFIG_COMPAT + if (sisusb->ioctl32registered) { + int ret; + sisusb->ioctl32registered = 0; + ret = unregister_ioctl32_conversion(SISUSB_GET_CONFIG_SIZE); + ret |= unregister_ioctl32_conversion(SISUSB_GET_CONFIG); + ret |= unregister_ioctl32_conversion(SISUSB_COMMAND); + if (ret) { + printk(KERN_ERR + "sisusbvga[%d]: Error unregistering " + "ioctl32 translations\n", + minor); + } + } +#endif + + sisusb->present = 0; + sisusb->ready = 0; + + up(&sisusb->lock); + + /* decrement our usage count */ + kref_put(&sisusb->kref, sisusb_delete); + + up(&disconnect_sem); + + printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor); +} + +static struct usb_device_id sisusb_table [] = { + { USB_DEVICE(0x0711, 0x0900) }, + { } +}; + +MODULE_DEVICE_TABLE (usb, sisusb_table); + +static struct usb_driver sisusb_driver = { + .owner = THIS_MODULE, + .name = "sisusb", + .probe = sisusb_probe, + .disconnect = sisusb_disconnect, + .id_table = sisusb_table +}; + +static int __init usb_sisusb_init(void) +{ + int retval; + + if (!(retval = usb_register(&sisusb_driver))) { + printk(KERN_INFO "sisusb: Driver version %d.%d.%d\n", + SISUSB_VERSION, SISUSB_REVISION, SISUSB_PATCHLEVEL); + printk(KERN_INFO + "sisusb: Copyright (C) 2005 Thomas Winischhofer\n"); + } + + return retval; +} + +static void __exit usb_sisusb_exit(void) +{ + usb_deregister(&sisusb_driver); +} + +module_init(usb_sisusb_init); +module_exit(usb_sisusb_exit); + +MODULE_AUTHOR("Thomas Winischhofer "); +MODULE_DESCRIPTION("sisusb - Driver for Net2280/SiS315-based USB2VGA dongles"); +MODULE_LICENSE("GPL"); + diff -Nru a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/misc/sisusbvga/sisusb.h 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,278 @@ +/* + * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles + * + * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria + * + * If distributed as part of the Linux kernel, this code is licensed under the + * terms of the GPL v2. + * + * Otherwise, the following license terms apply: + * + * * Redistribution and use in source and binary forms, with or without + * * modification, are permitted provided that the following conditions + * * are met: + * * 1) Redistributions of source code must retain the above copyright + * * notice, this list of conditions and the following disclaimer. + * * 2) Redistributions in binary form must reproduce the above copyright + * * notice, this list of conditions and the following disclaimer in the + * * documentation and/or other materials provided with the distribution. + * * 3) The name of the author may not be used to endorse or promote products + * * derived from this software without specific prior written permission. + * * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR + * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Author: Thomas Winischhofer + * + */ + +#ifndef _SISUSB_H_ +#define _SISUSB_H_ + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,10) +#include +#define SISUSB_OLD_CONFIG_COMPAT +#else +#define SISUSB_NEW_CONFIG_COMPAT +#endif +#endif + +/* Version Information */ + +#define SISUSB_VERSION 0 +#define SISUSB_REVISION 0 +#define SISUSB_PATCHLEVEL 7 + +/* USB related */ + +#define SISUSB_MINOR 133 /* FIXME */ + +/* Size of the sisusb input/output buffers */ +#define SISUSB_IBUF_SIZE 0x01000 +#define SISUSB_OBUF_SIZE 0x10000 /* fixed */ + +#define NUMOBUFS 8 /* max number of output buffers/output URBs */ + +/* About endianness: + * + * 1) I/O ports, PCI config registers. The read/write() + * calls emulate inX/outX. Hence, the data is + * expected/delivered in machine endiannes by this + * driver. + * 2) Video memory. The data is copied 1:1. There is + * no swapping. Ever. This means for userland that + * the data has to be prepared properly. (Hint: + * think graphics data format, command queue, + * hardware cursor.) + * 3) MMIO. Data is copied 1:1. MMIO must be swapped + * properly by userland. + * + */ + +#ifdef __BIG_ENDIAN +#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) \ + do { \ + p->header = cpu_to_le16(p->header); \ + p->address = cpu_to_le32(p->address); \ + p->data = cpu_to_le32(p->data); \ + } while(0) +#else +#define SISUSB_CORRECT_ENDIANNESS_PACKET(p) +#endif + +struct sisusb_usb_data; + +struct sisusb_urb_context { /* urb->context for outbound bulk URBs */ + struct sisusb_usb_data *sisusb; + int urbindex; + int *actual_length; +}; + +struct sisusb_usb_data { + struct usb_device *sisusb_dev; + struct usb_interface *interface; + struct kref kref; + wait_queue_head_t wait_q; /* for syncind and timeouts */ + struct semaphore lock; /* general race avoidance */ + unsigned int ifnum; /* interface number of the USB device */ + int minor; /* minor (for logging clarity) */ + int isopen; /* !=0 if open */ + int present; /* !=0 if device is present on the bus */ + int ready; /* !=0 if device is ready for userland */ +#ifdef SISUSB_OLD_CONFIG_COMPAT + int ioctl32registered; +#endif + int numobufs; /* number of obufs = number of out urbs */ + char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */ + int obufsize, ibufsize; + dma_addr_t transfer_dma_out[NUMOBUFS]; + dma_addr_t transfer_dma_in; + struct urb *sisurbout[NUMOBUFS]; + struct urb *sisurbin; + unsigned char urbstatus[NUMOBUFS]; + unsigned char completein; + struct sisusb_urb_context urbout_context[NUMOBUFS]; + unsigned long flagb0; + unsigned long vrambase; /* framebuffer base */ + unsigned int vramsize; /* framebuffer size (bytes) */ + unsigned long mmiobase; + unsigned int mmiosize; + unsigned long ioportbase; + unsigned char devinit; /* device initialized? */ + unsigned char gfxinit; /* graphics core initialized? */ + unsigned short chipid, chipvendor; + unsigned short chiprevision; +}; + +#define to_sisusb_dev(d) container_of(d, struct sisusb_usb_data, kref) + +/* USB transport related */ + +/* urbstatus */ +#define SU_URB_BUSY 1 +#define SU_URB_ALLOC 2 + +/* Endpoints */ + +#define SISUSB_EP_GFX_IN 0x0e /* gfx std packet out(0e)/in(8e) */ +#define SISUSB_EP_GFX_OUT 0x0e + +#define SISUSB_EP_GFX_BULK_OUT 0x01 /* gfx mem bulk out/in */ +#define SISUSB_EP_GFX_BULK_IN 0x02 /* ? 2 is "OUT" ? */ + +#define SISUSB_EP_GFX_LBULK_OUT 0x03 /* gfx large mem bulk out */ + +#define SISUSB_EP_UNKNOWN_04 0x04 /* ? 4 is "OUT" ? - unused */ + +#define SISUSB_EP_BRIDGE_IN 0x0d /* Net2280 out(0d)/in(8d) */ +#define SISUSB_EP_BRIDGE_OUT 0x0d + +#define SISUSB_TYPE_MEM 0 +#define SISUSB_TYPE_IO 1 + +struct sisusb_packet { + unsigned short header; + u32 address; + u32 data; +} __attribute__((__packed__)); + +#define CLEARPACKET(packet) memset(packet, 0, 10) + +/* PCI bridge related */ + +#define SISUSB_PCI_MEMBASE 0xd0000000 +#define SISUSB_PCI_MMIOBASE 0xe4000000 +#define SISUSB_PCI_IOPORTBASE 0x0000d000 + +#define SISUSB_PCI_PSEUDO_MEMBASE 0x10000000 +#define SISUSB_PCI_PSEUDO_MMIOBASE 0x20000000 +#define SISUSB_PCI_PSEUDO_IOPORTBASE 0x0000d000 +#define SISUSB_PCI_PSEUDO_PCIBASE 0x00010000 + +#define SISUSB_PCI_MMIOSIZE (128*1024) +#define SISUSB_PCI_PCONFSIZE 0x5c + +/* graphics core related */ + +#define AROFFSET 0x40 +#define ARROFFSET 0x41 +#define GROFFSET 0x4e +#define SROFFSET 0x44 +#define CROFFSET 0x54 +#define MISCROFFSET 0x4c +#define MISCWOFFSET 0x42 +#define INPUTSTATOFFSET 0x5A +#define PART1OFFSET 0x04 +#define PART2OFFSET 0x10 +#define PART3OFFSET 0x12 +#define PART4OFFSET 0x14 +#define PART5OFFSET 0x16 +#define CAPTUREOFFSET 0x00 +#define VIDEOOFFSET 0x02 +#define COLREGOFFSET 0x48 +#define PELMASKOFFSET 0x46 +#define VGAENABLE 0x43 + +#define SISAR SISUSB_PCI_IOPORTBASE + AROFFSET +#define SISARR SISUSB_PCI_IOPORTBASE + ARROFFSET +#define SISGR SISUSB_PCI_IOPORTBASE + GROFFSET +#define SISSR SISUSB_PCI_IOPORTBASE + SROFFSET +#define SISCR SISUSB_PCI_IOPORTBASE + CROFFSET +#define SISMISCR SISUSB_PCI_IOPORTBASE + MISCROFFSET +#define SISMISCW SISUSB_PCI_IOPORTBASE + MISCWOFFSET +#define SISINPSTAT SISUSB_PCI_IOPORTBASE + INPUTSTATOFFSET +#define SISPART1 SISUSB_PCI_IOPORTBASE + PART1OFFSET +#define SISPART2 SISUSB_PCI_IOPORTBASE + PART2OFFSET +#define SISPART3 SISUSB_PCI_IOPORTBASE + PART3OFFSET +#define SISPART4 SISUSB_PCI_IOPORTBASE + PART4OFFSET +#define SISPART5 SISUSB_PCI_IOPORTBASE + PART5OFFSET +#define SISCAP SISUSB_PCI_IOPORTBASE + CAPTUREOFFSET +#define SISVID SISUSB_PCI_IOPORTBASE + VIDEOOFFSET +#define SISCOLIDXR SISUSB_PCI_IOPORTBASE + COLREGOFFSET - 1 +#define SISCOLIDX SISUSB_PCI_IOPORTBASE + COLREGOFFSET +#define SISCOLDATA SISUSB_PCI_IOPORTBASE + COLREGOFFSET + 1 +#define SISCOL2IDX SISPART5 +#define SISCOL2DATA SISPART5 + 1 +#define SISPEL SISUSB_PCI_IOPORTBASE + PELMASKOFFSET +#define SISVGAEN SISUSB_PCI_IOPORTBASE + VGAENABLE +#define SISDACA SISCOLIDX +#define SISDACD SISCOLDATA + +/* ioctl related */ + +/* Structure argument for SISUSB_GET_INFO ioctl */ +struct sisusb_info { + __u32 sisusb_id; /* for identifying sisusb */ +#define SISUSB_ID 0x53495355 /* Identify myself with 'SISU' */ + __u8 sisusb_version; + __u8 sisusb_revision; + __u8 sisusb_patchlevel; + __u8 sisusb_gfxinit; /* graphics core initialized? */ + + __u32 sisusb_vrambase; + __u32 sisusb_mmiobase; + __u32 sisusb_iobase; + __u32 sisusb_pcibase; + + __u32 sisusb_vramsize; /* framebuffer size in bytes */ + + __u32 sisusb_minor; + + __u32 sisusb_fbdevactive; /* != 0 if framebuffer device active */ + + __u8 sisusb_reserved[32]; /* for future use */ +}; + +struct sisusb_command { + __u8 operation; /* see below */ + __u8 data0; /* operation dependent */ + __u8 data1; /* operation dependent */ + __u8 data2; /* operation dependent */ + __u32 data3; /* operation dependent */ + __u32 data4; /* for future use */ +}; + +#define SUCMD_GET 0x01 /* for all: data0 = index, data3 = port */ +#define SUCMD_SET 0x02 /* data1 = value */ +#define SUCMD_SETOR 0x03 /* data1 = or */ +#define SUCMD_SETAND 0x04 /* data1 = and */ +#define SUCMD_SETANDOR 0x05 /* data1 = and, data2 = or */ +#define SUCMD_SETMASK 0x06 /* data1 = data, data2 = mask */ + +#define SUCMD_CLRSCR 0x07 /* data0:1:2 = length, data3 = address */ + +#define SISUSB_COMMAND _IOWR(0xF3,0x3D,struct sisusb_command) +#define SISUSB_GET_CONFIG_SIZE _IOR(0xF3,0x3E,__u32) +#define SISUSB_GET_CONFIG _IOR(0xF3,0x3F,struct sisusb_info) + +#endif /* SISUSB_H */ + diff -Nru a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/Kconfig 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,22 @@ +# +# USB Monitor configuration +# + +# In normal life, it makes little sense to have usbmon as a module, and in fact +# it is harmful, because there is no way to autoload the module. +# The 'm' option is allowed for hackers who debug the usbmon itself, +# and for those who have usbcore as a module. +config USB_MON + tristate "USB Monitor" + depends on USB + default y + help + If you say Y here, a component which captures the USB traffic + between peripheral-specific drivers and HC drivers will be built. + The USB_MON is similar in spirit and may be compatible with Dave + Harding's USBMon. + + This is somewhat experimental at this time, but it should be safe, + as long as you aren't building this as a module and then removing it. + + If unsure, say Y. Do not say M. diff -Nru a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/Makefile 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,7 @@ +# +# Makefile for USB Core files and filesystem +# + +usbmon-objs := mon_main.o mon_stat.o mon_text.o + +obj-$(CONFIG_USB_MON) += usbmon.o diff -Nru a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/mon_main.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,377 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + * + * mon_main.c: Main file, module initiation and exit, registrations, etc. + */ + +#include +#include +#include +#include +#include + +#include "usb_mon.h" +#include "../core/hcd.h" + +static void mon_submit(struct usb_bus *ubus, struct urb *urb); +static void mon_complete(struct usb_bus *ubus, struct urb *urb); +static void mon_stop(struct mon_bus *mbus); +static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); +static void mon_bus_drop(struct kref *r); +static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus); + +DECLARE_MUTEX(mon_lock); + +static struct dentry *mon_dir; /* /dbg/usbmon */ +static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ + +/* + * Link a reader into the bus. + * + * This must be called with mon_lock taken because of mbus->ref. + */ +void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) +{ + unsigned long flags; + struct usb_bus *ubus; + + spin_lock_irqsave(&mbus->lock, flags); + if (mbus->nreaders == 0) { + ubus = mbus->u_bus; + if (ubus->monitored) { + /* + * Something is really broken, refuse to go on and + * possibly corrupt ops pointers or worse. + */ + printk(KERN_ERR TAG ": bus %d is already monitored\n", + ubus->busnum); + spin_unlock_irqrestore(&mbus->lock, flags); + return; + } + ubus->monitored = 1; + } + mbus->nreaders++; + list_add_tail(&r->r_link, &mbus->r_list); + spin_unlock_irqrestore(&mbus->lock, flags); + + kref_get(&mbus->ref); +} + +/* + * Unlink reader from the bus. + * + * This is called with mon_lock taken, so we can decrement mbus->ref. + */ +void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) +{ + unsigned long flags; + + spin_lock_irqsave(&mbus->lock, flags); + list_del(&r->r_link); + --mbus->nreaders; + if (mbus->nreaders == 0) + mon_stop(mbus); + spin_unlock_irqrestore(&mbus->lock, flags); + + kref_put(&mbus->ref, mon_bus_drop); +} + +/* + */ +static void mon_submit(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + unsigned long flags; + struct list_head *pos; + struct mon_reader *r; + + mbus = ubus->mon_bus; + if (mbus == NULL) + goto out_unlocked; + + spin_lock_irqsave(&mbus->lock, flags); + if (mbus->nreaders == 0) + goto out_locked; + + list_for_each (pos, &mbus->r_list) { + r = list_entry(pos, struct mon_reader, r_link); + r->rnf_submit(r->r_data, urb); + } + + spin_unlock_irqrestore(&mbus->lock, flags); + return; + +out_locked: + spin_unlock_irqrestore(&mbus->lock, flags); +out_unlocked: + return; +} + +/* + */ +static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int err) +{ + struct mon_bus *mbus; + + mbus = ubus->mon_bus; + if (mbus == NULL) + goto out_unlocked; + + /* + * XXX Capture the error code and the 'E' event. + */ + + return; + +out_unlocked: + return; +} + +/* + */ +static void mon_complete(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + unsigned long flags; + struct list_head *pos; + struct mon_reader *r; + + mbus = ubus->mon_bus; + if (mbus == NULL) { + /* + * This should not happen. + * At this point we do not even know the bus number... + */ + printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n", + urb->pipe); + return; + } + + spin_lock_irqsave(&mbus->lock, flags); + list_for_each (pos, &mbus->r_list) { + r = list_entry(pos, struct mon_reader, r_link); + r->rnf_complete(r->r_data, urb); + } + spin_unlock_irqrestore(&mbus->lock, flags); +} + +/* int (*unlink_urb) (struct urb *urb, int status); */ + +/* + * Stop monitoring. + * Obviously this must be well locked, so no need to play with mb's. + */ +static void mon_stop(struct mon_bus *mbus) +{ + struct usb_bus *ubus = mbus->u_bus; + + /* + * A stop can be called for a dissolved mon_bus in case of + * a reader staying across an rmmod foo_hcd. + */ + if (ubus != NULL) { + ubus->monitored = 0; + mb(); + } +} + +/* + * Add a USB bus (usually by a modprobe foo-hcd) + * + * This does not return an error code because the core cannot care less + * if monitoring is not established. + */ +static void mon_bus_add(struct usb_bus *ubus) +{ + mon_bus_init(mon_dir, ubus); +} + +/* + * Remove a USB bus (either from rmmod foo-hcd or from a hot-remove event). + */ +static void mon_bus_remove(struct usb_bus *ubus) +{ + struct mon_bus *mbus = ubus->mon_bus; + + down(&mon_lock); + list_del(&mbus->bus_link); + debugfs_remove(mbus->dent_t); + debugfs_remove(mbus->dent_s); + + mon_dissolve(mbus, ubus); + kref_put(&mbus->ref, mon_bus_drop); + up(&mon_lock); +} + +/* + * Ops + */ +static struct usb_mon_operations mon_ops_0 = { + .urb_submit = mon_submit, + .urb_submit_error = mon_submit_error, + .urb_complete = mon_complete, + .bus_add = mon_bus_add, + .bus_remove = mon_bus_remove, +}; + +/* + * Tear usb_bus and mon_bus apart. + */ +static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) +{ + + /* + * Never happens, but... + */ + if (ubus->monitored) { + printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", + ubus->busnum); + ubus->monitored = 0; + mb(); + } + + ubus->mon_bus = NULL; + mbus->u_bus = NULL; + mb(); + // usb_bus_put(ubus); +} + +/* + */ +static void mon_bus_drop(struct kref *r) +{ + struct mon_bus *mbus = container_of(r, struct mon_bus, ref); + kfree(mbus); +} + +/* + * Initialize a bus for us: + * - allocate mon_bus + * - refcount USB bus struct + * - link + */ +static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus) +{ + struct dentry *d; + struct mon_bus *mbus; + enum { NAMESZ = 10 }; + char name[NAMESZ]; + int rc; + + if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) + goto err_alloc; + memset(mbus, 0, sizeof(struct mon_bus)); + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + /* + * This usb_bus_get here is superfluous, because we receive + * a notification if usb_bus is about to be removed. + */ + // usb_bus_get(ubus); + mbus->u_bus = ubus; + ubus->mon_bus = mbus; + + rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_t; + d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text); + if (d == NULL) + goto err_create_t; + mbus->dent_t = d; + + rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_s; + d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat); + if (d == NULL) + goto err_create_s; + mbus->dent_s = d; + + down(&mon_lock); + list_add_tail(&mbus->bus_link, &mon_buses); + up(&mon_lock); + return; + +err_create_s: +err_print_s: + debugfs_remove(mbus->dent_t); +err_create_t: +err_print_t: + kfree(mbus); +err_alloc: + return; +} + +static int __init mon_init(void) +{ + struct usb_bus *ubus; + struct dentry *mondir; + + mondir = debugfs_create_dir("usbmon", NULL); + if (IS_ERR(mondir)) { + printk(KERN_NOTICE TAG ": debugs is not available\n"); + return -ENODEV; + } + if (mondir == NULL) { + printk(KERN_NOTICE TAG ": unable to create usbmon directory\n"); + return -ENODEV; + } + mon_dir = mondir; + + if (usb_mon_register(&mon_ops_0) != 0) { + printk(KERN_NOTICE TAG ": unable to register with the core\n"); + debugfs_remove(mondir); + return -ENODEV; + } + // MOD_INC_USE_COUNT(which_module?); + + down(&usb_bus_list_lock); + list_for_each_entry (ubus, &usb_bus_list, bus_list) { + mon_bus_init(mondir, ubus); + } + up(&usb_bus_list_lock); + return 0; +} + +static void __exit mon_exit(void) +{ + struct mon_bus *mbus; + struct list_head *p; + + usb_mon_deregister(); + + down(&mon_lock); + while (!list_empty(&mon_buses)) { + p = mon_buses.next; + mbus = list_entry(p, struct mon_bus, bus_link); + list_del(p); + + debugfs_remove(mbus->dent_t); + debugfs_remove(mbus->dent_s); + + /* + * This never happens, because the open/close paths in + * file level maintain module use counters and so rmmod fails + * before reaching here. However, better be safe... + */ + if (mbus->nreaders) { + printk(KERN_ERR TAG + ": Outstanding opens (%d) on usb%d, leaking...\n", + mbus->nreaders, mbus->u_bus->busnum); + atomic_set(&mbus->ref.refcount, 2); /* Force leak */ + } + + mon_dissolve(mbus, mbus->u_bus); + kref_put(&mbus->ref, mon_bus_drop); + } + up(&mon_lock); + + debugfs_remove(mon_dir); +} + +module_init(mon_init); +module_exit(mon_exit); + +MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/mon_stat.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,74 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + * + * This is the 's' or 'stat' reader which debugs usbmon itself. + * Note that this code blows through locks, so make sure that + * /dbg/usbmon/0s is well protected from non-root users. + * + */ + +#include +#include +#include + +#include "usb_mon.h" + +#define STAT_BUF_SIZE 80 + +struct snap { + int slen; + char str[STAT_BUF_SIZE]; +}; + +static int mon_stat_open(struct inode *inode, struct file *file) +{ + struct mon_bus *mbus; + struct snap *sp; + + if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL) + return -ENOMEM; + + mbus = inode->u.generic_ip; + + sp->slen = snprintf(sp->str, STAT_BUF_SIZE, + "nreaders %d text_lost %u\n", + mbus->nreaders, mbus->cnt_text_lost); + + file->private_data = sp; + return 0; +} + +static ssize_t mon_stat_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct snap *sp = file->private_data; + loff_t pos = *ppos; + int cnt; + + if (pos < 0 || pos >= sp->slen) + return 0; + if (nbytes == 0) + return 0; + if ((cnt = sp->slen - pos) > nbytes) + cnt = nbytes; + if (copy_to_user(buf, sp->str + pos, cnt)) + return -EFAULT; + *ppos = pos + cnt; + return cnt; +} + +static int mon_stat_release(struct inode *inode, struct file *file) +{ + return 0; +} + +struct file_operations mon_fops_stat = { + .owner = THIS_MODULE, + .open = mon_stat_open, + .llseek = no_llseek, + .read = mon_stat_read, + /* .write = mon_stat_write, */ + /* .poll = mon_stat_poll, */ + /* .ioctl = mon_stat_ioctl, */ + .release = mon_stat_release, +}; diff -Nru a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/mon_text.c 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,395 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + * + * This is a text format reader. + */ + +#include +#include +#include +#include +#include + +#include "usb_mon.h" + +/* + * No, we do not want arbitrarily long data strings. + * Use the binary interface if you want to capture bulk data! + */ +#define DATA_MAX 32 + +/* + * This limit exists to prevent OOMs when the user process stops reading. + */ +#define EVENT_MAX 25 + +#define PRINTF_DFL 120 + +struct mon_event_text { + struct list_head e_link; + int type; /* submit, complete, etc. */ + unsigned int pipe; /* Pipe */ + unsigned long id; /* From pointer, most of the time */ + unsigned int tstamp; + int length; /* Depends on type: xfer length or act length */ + int status; + char data_flag; + unsigned char data[DATA_MAX]; +}; + +#define SLAB_NAME_SZ 30 +struct mon_reader_text { + kmem_cache_t *e_slab; + int nevents; + struct list_head e_list; + struct mon_reader r; /* In C, parent class can be placed anywhere */ + + wait_queue_head_t wait; + int printf_size; + char *printf_buf; + struct semaphore printf_lock; + + char slab_name[SLAB_NAME_SZ]; +}; + +static void mon_text_ctor(void *, kmem_cache_t *, unsigned long); +static void mon_text_dtor(void *, kmem_cache_t *, unsigned long); + +/* + * mon_text_submit + * mon_text_complete + * + * May be called from an interrupt. + * + * This is called with the whole mon_bus locked, so no additional lock. + */ + +static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, + int len, char ev_type) +{ + int pipe = urb->pipe; + unsigned char *data; + + /* + * The check to see if it's safe to poke at data has an enormous + * number of corner cases, but it seems that the following is + * more or less safe. + * + * We do not even try to look transfer_buffer, because it can + * contain non-NULL garbage in case the upper level promised to + * set DMA for the HCD. + */ + if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) + return 'D'; + + if (len <= 0) + return 'L'; + + if ((data = urb->transfer_buffer) == NULL) + return 'Z'; /* '0' would be not as pretty. */ + + /* + * Bulk is easy to shortcut reliably. + * XXX Control needs setup packet taken. + * XXX Other pipe types need consideration. Currently, we overdo it + * and collect garbage for them: better more than less. + */ + if (usb_pipebulk(pipe) || usb_pipecontrol(pipe)) { + if (usb_pipein(pipe)) { + if (ev_type == 'S') + return '<'; + } else { + if (ev_type == 'C') + return '>'; + } + } + + if (len >= DATA_MAX) + len = DATA_MAX; + memcpy(ep->data, urb->transfer_buffer, len); + return 0; +} + +static inline unsigned int mon_get_timestamp(void) +{ + struct timeval tval; + unsigned int stamp; + + do_gettimeofday(&tval); + stamp = tval.tv_sec & 0xFFFF; /* 2^32 = 4294967296. Limit to 4096s. */ + stamp = stamp * 1000000 + tval.tv_usec; + return stamp; +} + +static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, + char ev_type) +{ + struct mon_event_text *ep; + unsigned int stamp; + + stamp = mon_get_timestamp(); + + if (rp->nevents >= EVENT_MAX || + (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) { + rp->r.m_bus->cnt_text_lost++; + return; + } + + ep->type = ev_type; + ep->pipe = urb->pipe; + ep->id = (unsigned long) urb; + ep->tstamp = stamp; + ep->length = (ev_type == 'S') ? + urb->transfer_buffer_length : urb->actual_length; + /* Collecting status makes debugging sense for submits, too */ + ep->status = urb->status; + + ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type); + + rp->nevents++; + list_add_tail(&ep->e_link, &rp->e_list); + wake_up(&rp->wait); +} + +static void mon_text_submit(void *data, struct urb *urb) +{ + struct mon_reader_text *rp = data; + mon_text_event(rp, urb, 'S'); +} + +static void mon_text_complete(void *data, struct urb *urb) +{ + struct mon_reader_text *rp = data; + mon_text_event(rp, urb, 'C'); +} + +/* + * Fetch next event from the circular buffer. + */ +static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, + struct mon_bus *mbus) +{ + struct list_head *p; + unsigned long flags; + + spin_lock_irqsave(&mbus->lock, flags); + if (list_empty(&rp->e_list)) { + spin_unlock_irqrestore(&mbus->lock, flags); + return NULL; + } + p = rp->e_list.next; + list_del(p); + --rp->nevents; + spin_unlock_irqrestore(&mbus->lock, flags); + return list_entry(p, struct mon_event_text, e_link); +} + +/* + */ +static int mon_text_open(struct inode *inode, struct file *file) +{ + struct mon_bus *mbus; + struct usb_bus *ubus; + struct mon_reader_text *rp; + int rc; + + down(&mon_lock); + mbus = inode->u.generic_ip; + ubus = mbus->u_bus; + + rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL); + if (rp == NULL) { + rc = -ENOMEM; + goto err_alloc; + } + memset(rp, 0, sizeof(struct mon_reader_text)); + INIT_LIST_HEAD(&rp->e_list); + init_waitqueue_head(&rp->wait); + init_MUTEX(&rp->printf_lock); + + rp->printf_size = PRINTF_DFL; + rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL); + if (rp->printf_buf == NULL) { + rc = -ENOMEM; + goto err_alloc_pr; + } + + rp->r.m_bus = mbus; + rp->r.r_data = rp; + rp->r.rnf_submit = mon_text_submit; + rp->r.rnf_complete = mon_text_complete; + + snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum, + (long)rp); + rp->e_slab = kmem_cache_create(rp->slab_name, + sizeof(struct mon_event_text), sizeof(long), 0, + mon_text_ctor, mon_text_dtor); + if (rp->e_slab == NULL) { + rc = -ENOMEM; + goto err_slab; + } + + mon_reader_add(mbus, &rp->r); + + file->private_data = rp; + up(&mon_lock); + return 0; + +// err_busy: +// kmem_cache_destroy(rp->e_slab); +err_slab: + kfree(rp->printf_buf); +err_alloc_pr: + kfree(rp); +err_alloc: + up(&mon_lock); + return rc; +} + +/* + * For simplicity, we read one record in one system call and throw out + * what does not fit. This means that the following does not work: + * dd if=/dbg/usbmon/0t bs=10 + * Also, we do not allow seeks and do not bother advancing the offset. + */ +static ssize_t mon_text_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_bus *mbus = rp->r.m_bus; + DECLARE_WAITQUEUE(waita, current); + struct mon_event_text *ep; + int cnt, limit; + char *pbuf; + int data_len, i; + + add_wait_queue(&rp->wait, &waita); + set_current_state(TASK_INTERRUPTIBLE); + while ((ep = mon_text_fetch(rp, mbus)) == NULL) { + if (file->f_flags & O_NONBLOCK) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + } + /* + * We do not count nwaiters, because ->release is supposed + * to be called when all openers are gone only. + */ + schedule(); + if (signal_pending(current)) { + remove_wait_queue(&rp->wait, &waita); + return -EINTR; + } + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&rp->wait, &waita); + + down(&rp->printf_lock); + cnt = 0; + pbuf = rp->printf_buf; + limit = rp->printf_size; + + cnt += snprintf(pbuf + cnt, limit - cnt, + "%lx %u %c %08x %d %d", + ep->id, ep->tstamp, ep->type, ep->pipe, ep->status, ep->length); + + if ((data_len = ep->length) > 0) { + if (ep->data_flag == 0) { + cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + if (data_len >= DATA_MAX) + data_len = DATA_MAX; + for (i = 0; i < data_len; i++) { + if (i % 4 == 0) { + cnt += snprintf(pbuf + cnt, limit - cnt, + " "); + } + cnt += snprintf(pbuf + cnt, limit - cnt, + "%02x", ep->data[i]); + } + cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + } else { + cnt += snprintf(pbuf + cnt, limit - cnt, + " %c\n", ep->data_flag); + } + } else { + cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + } + + if (copy_to_user(buf, rp->printf_buf, cnt)) + cnt = -EFAULT; + up(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return cnt; +} + +static int mon_text_release(struct inode *inode, struct file *file) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_bus *mbus; + /* unsigned long flags; */ + struct list_head *p; + struct mon_event_text *ep; + + down(&mon_lock); + mbus = inode->u.generic_ip; + + if (mbus->nreaders <= 0) { + printk(KERN_ERR TAG ": consistency error on close\n"); + up(&mon_lock); + return 0; + } + mon_reader_del(mbus, &rp->r); + + /* + * In theory, e_list is protected by mbus->lock. However, + * after mon_reader_del has finished, the following is the case: + * - we are not on reader list anymore, so new events won't be added; + * - whole mbus may be dropped if it was orphaned. + * So, we better not touch mbus. + */ + /* spin_lock_irqsave(&mbus->lock, flags); */ + while (!list_empty(&rp->e_list)) { + p = rp->e_list.next; + ep = list_entry(p, struct mon_event_text, e_link); + list_del(p); + --rp->nevents; + kmem_cache_free(rp->e_slab, ep); + } + /* spin_unlock_irqrestore(&mbus->lock, flags); */ + + kmem_cache_destroy(rp->e_slab); + kfree(rp->printf_buf); + kfree(rp); + + up(&mon_lock); + return 0; +} + +struct file_operations mon_fops_text = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read, + /* .write = mon_text_write, */ + /* .poll = mon_text_poll, */ + /* .ioctl = mon_text_ioctl, */ + .release = mon_text_release, +}; + +/* + * Slab interface: constructor. + */ +static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags) +{ + /* + * Nothing to initialize. No, really! + * So, we fill it with garbage to emulate a reused object. + */ + memset(mem, 0xe5, sizeof(struct mon_event_text)); +} + +static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags) +{ + ; +} diff -Nru a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/usb/mon/usb_mon.h 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,51 @@ +/* + * The USB Monitor, inspired by Dave Harding's USBMon. + */ + +#ifndef __USB_MON_H +#define __USB_MON_H + +#include +#include +#include +/* #include */ /* We use struct pointers only in this header */ + +#define TAG "usbmon" + +struct mon_bus { + struct list_head bus_link; + spinlock_t lock; + struct dentry *dent_s; /* Debugging file */ + struct dentry *dent_t; /* Text interface file */ + struct usb_bus *u_bus; + + /* Ref */ + int nreaders; /* Under mon_lock AND mbus->lock */ + struct list_head r_list; /* Chain of readers (usually one) */ + struct kref ref; /* Under mon_lock */ + + /* Stats */ + unsigned int cnt_text_lost; +}; + +/* + * An instance of a process which opened a file (but can fork later) + */ +struct mon_reader { + struct list_head r_link; + struct mon_bus *m_bus; + void *r_data; /* Use container_of instead? */ + + void (*rnf_submit)(void *data, struct urb *urb); + void (*rnf_complete)(void *data, struct urb *urb); +}; + +void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r); +void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); + +extern struct semaphore mon_lock; + +extern struct file_operations mon_fops_text; +extern struct file_operations mon_fops_stat; + +#endif /* __USB_MON_H */ diff -Nru a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig --- a/drivers/usb/net/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/net/Kconfig 2005-02-04 22:22:37 -08:00 @@ -260,13 +260,13 @@ depends on USB_USBNET config USB_AX8817X - boolean "ASIX AX88172 Based USB 2.0 Ethernet Devices" + boolean "ASIX AX88xxx Based USB 2.0 Ethernet Devices" depends on USB_USBNET && NET_ETHERNET select CRC32 select MII default y help - This option adds support for ASIX AX88172 based USB 2.0 + This option adds support for ASIX AX88xxx based USB 2.0 10/100 Ethernet devices. This driver should work with at least the following devices: diff -Nru a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c --- a/drivers/usb/net/kaweth.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/net/kaweth.c 2005-02-04 22:22:37 -08:00 @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include @@ -1180,31 +1181,21 @@ // Starts urb and waits for completion or timeout static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) { - DECLARE_WAITQUEUE(wait, current); struct usb_api_data awd; int status; init_waitqueue_head(&awd.wqh); awd.done = 0; - add_wait_queue(&awd.wqh, &wait); urb->context = &awd; status = usb_submit_urb(urb, GFP_NOIO); if (status) { // something went wrong usb_free_urb(urb); - remove_wait_queue(&awd.wqh, &wait); return status; } - while (timeout && !awd.done) { - set_current_state(TASK_UNINTERRUPTIBLE); - timeout = schedule_timeout(timeout); - } - - remove_wait_queue(&awd.wqh, &wait); - - if (!timeout) { + if (!wait_event_timeout(awd.wqh, awd.done, timeout)) { // timeout kaweth_warn("usb_control/bulk_msg: timeout"); usb_kill_urb(urb); // remove urb safely diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c --- a/drivers/usb/net/usbnet.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/net/usbnet.c 2005-02-04 22:22:37 -08:00 @@ -2,7 +2,8 @@ * USB Networking Links * Copyright (C) 2000-2003 by David Brownell * Copyright (C) 2002 Pavel Machek - * Copyright (C) 2003 David Hollis + * Copyright (C) 2003-2005 David Hollis + * Copyright (C) 2005 Phil Chang * Copyright (c) 2002-2003 TiVo Inc. * * This program is free software; you can redistribute it and/or modify @@ -109,6 +110,7 @@ * (Neil Bortnak) * 03-nov-2004 Trivial patch for KC2190 (KC-190) chip. (Jonathan McDowell) * + * 01-feb-2005 AX88772 support (Phil Chang & Dave Hollis) *-------------------------------------------------------------------------*/ // #define DEBUG // error path messages, extra info @@ -222,6 +224,8 @@ #define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */ #define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */ +#define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */ + /* init device ... can sleep, or cause probe() failure */ int (*bind)(struct usbnet *, struct usb_interface *); @@ -274,9 +278,6 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)"); -#define RUN_CONTEXT (in_irq () ? "in_irq" \ - : (in_interrupt () ? "in_interrupt" : "can sleep")) - #ifdef DEBUG #define devdbg(usbnet, fmt, arg...) \ printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg) @@ -435,6 +436,8 @@ #define AX_CMD_SET_HW_MII 0x0a #define AX_CMD_READ_EEPROM 0x0b #define AX_CMD_WRITE_EEPROM 0x0c +#define AX_CMD_WRITE_ENABLE 0x0d +#define AX_CMD_WRITE_DISABLE 0x0e #define AX_CMD_WRITE_RX_CTL 0x10 #define AX_CMD_READ_IPG012 0x11 #define AX_CMD_WRITE_IPG0 0x12 @@ -447,6 +450,10 @@ #define AX_CMD_READ_MONITOR_MODE 0x1c #define AX_CMD_WRITE_MONITOR_MODE 0x1d #define AX_CMD_WRITE_GPIOS 0x1f +#define AX_CMD_SW_RESET 0x20 +#define AX_CMD_SW_PHY_STATUS 0x21 +#define AX_CMD_SW_PHY_SELECT 0x22 +#define AX88772_CMD_READ_NODE_ID 0x13 #define AX_MONITOR_MODE 0x01 #define AX_MONITOR_LINK 0x02 @@ -458,6 +465,23 @@ #define AX_INTERRUPT_BUFSIZE 8 +#define AX_EEPROM_LEN 0x40 + +#define AX_SWRESET_CLEAR 0x00 +#define AX_SWRESET_RR 0x01 +#define AX_SWRESET_RT 0x02 +#define AX_SWRESET_PRTE 0x04 +#define AX_SWRESET_PRL 0x08 +#define AX_SWRESET_BZ 0x10 +#define AX_SWRESET_IPRL 0x20 +#define AX_SWRESET_IPPD 0x40 + +#define AX88772_IPG0_DEFAULT 0x15 +#define AX88772_IPG1_DEFAULT 0x0c +#define AX88772_IPG2_DEFAULT 0x12 + +#define AX_EEPROM_MAGIC 0xdeadbeef + /* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */ struct ax8817x_data { u8 multi_filter[AX_MCAST_FILTER_SIZE]; @@ -514,18 +538,16 @@ int link; if (urb->status < 0) { - printk(KERN_DEBUG "ax8817x_interrupt_complete() failed with %d", + devdbg(dev,"ax8817x_interrupt_complete() failed with %d", urb->status); } else { - if (data->int_buf[5] == 0x90) { - link = data->int_buf[2] & 0x01; - if (netif_carrier_ok(dev->net) != link) { - if (link) - netif_carrier_on(dev->net); - else - netif_carrier_off(dev->net); - devdbg(dev, "ax8817x - Link Status is: %d", link); - } + link = data->int_buf[2] & 0x01; + if (netif_carrier_ok(dev->net) != link) { + if (link) + netif_carrier_on(dev->net); + else + netif_carrier_off(dev->net); + devdbg(dev, "ax8817x - Link Status is: %d", link); } usb_submit_urb(data->int_urb, GFP_ATOMIC); } @@ -674,6 +696,11 @@ return 0; } +static int ax8817x_get_eeprom_len(struct net_device *net) +{ + return AX_EEPROM_LEN; +} + static int ax8817x_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data) { @@ -687,13 +714,15 @@ if (eeprom->len % 2) return -EINVAL; + eeprom->magic = AX_EEPROM_MAGIC; + /* ax8817x returns 2 bytes from eeprom on read */ for (i=0; i < eeprom->len / 2; i++) { if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, eeprom->offset + i, 0, 2, &ebuf[i]) < 0) return -EINVAL; } - return i * 2; + return 0; } static void ax8817x_get_drvinfo (struct net_device *net, @@ -728,6 +757,7 @@ .set_msglevel = usbnet_set_msglevel, .get_wol = ax8817x_get_wol, .set_wol = ax8817x_set_wol, + .get_eeprom_len = ax8817x_get_eeprom_len, .get_eeprom = ax8817x_get_eeprom, .get_settings = ax8817x_get_settings, .set_settings = ax8817x_set_settings, @@ -735,27 +765,26 @@ static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf) { - int ret; - u8 buf[6]; + int ret = 0; + void *buf; int i; unsigned long gpio_bits = dev->driver_info->data; struct ax8817x_data *data = (struct ax8817x_data *)dev->data; - dev->in = usb_rcvbulkpipe(dev->udev, 3); - dev->out = usb_sndbulkpipe(dev->udev, 2); + get_endpoints(dev,intf); - // allocate irq urb if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) { dbg ("%s: cannot allocate interrupt URB", dev->net->name); - return -ENOMEM; + ret = -ENOMEM; + goto out1; } if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) { dbg ("%s: cannot allocate memory for interrupt buffer", dev->net->name); - usb_free_urb(data->int_urb); - return -ENOMEM; + ret = -ENOMEM; + goto out1; } memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE); @@ -765,36 +794,43 @@ ax8817x_interrupt_complete, dev, dev->udev->speed == USB_SPEED_HIGH ? 8 : 100); + buf = kmalloc(ETH_ALEN, GFP_KERNEL); + if(!buf) { + ret = -ENOMEM; + goto out2; + } + /* Toggle the GPIOs in a manufacturer/model specific way */ for (i = 2; i >= 0; i--) { if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, (gpio_bits >> (i * 8)) & 0xff, 0, 0, buf)) < 0) - return ret; + goto out3; msleep(5); } if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) { dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret); - return ret; + goto out3; } /* Get the MAC address */ memset(buf, 0, ETH_ALEN); if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) { dbg("read AX_CMD_READ_NODE_ID failed: %d", ret); - return ret; + goto out3; } memcpy(dev->net->dev_addr, buf, ETH_ALEN); /* Get the PHY id */ if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) { dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret); - return ret; + goto out3; } else if (ret < 2) { /* this should always return 2 bytes */ dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret); - return -EIO; + ret = -EIO; + goto out3; } /* Initialize MII structure */ @@ -803,7 +839,7 @@ dev->mii.mdio_write = ax8817x_mdio_write; dev->mii.phy_id_mask = 0x3f; dev->mii.reg_num_mask = 0x1f; - dev->mii.phy_id = buf[1]; + dev->mii.phy_id = *((u8 *)buf + 1); dev->net->set_multicast_list = ax8817x_set_multicast; dev->net->ethtool_ops = &ax8817x_ethtool_ops; @@ -816,11 +852,17 @@ if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) { dbg("Failed to submit interrupt URB: %02x", ret); - usb_free_urb(data->int_urb); - return ret; + goto out2; } return 0; +out3: + kfree(buf); +out2: + kfree(data->int_buf); +out1: + usb_free_urb(data->int_urb); + return ret; } static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf) @@ -832,6 +874,290 @@ kfree(data->int_buf); } +static struct ethtool_ops ax88772_ethtool_ops = { + .get_drvinfo = ax8817x_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_wol = ax8817x_get_wol, + .set_wol = ax8817x_set_wol, + .get_eeprom_len = ax8817x_get_eeprom_len, + .get_eeprom = ax8817x_get_eeprom, + .get_settings = ax8817x_get_settings, + .set_settings = ax8817x_set_settings, +}; + +static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int ret; + void *buf; + struct ax8817x_data *data = (struct ax8817x_data *)dev->data; + + get_endpoints(dev,intf); + + if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) { + dbg ("Cannot allocate interrupt URB"); + ret = -ENOMEM; + goto out1; + } + + if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) { + dbg ("Cannot allocate memory for interrupt buffer"); + ret = -ENOMEM; + goto out1; + } + memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE); + + usb_fill_int_urb (data->int_urb, dev->udev, + usb_rcvintpipe (dev->udev, 1), + data->int_buf, AX_INTERRUPT_BUFSIZE, + ax8817x_interrupt_complete, dev, + dev->udev->speed == USB_SPEED_HIGH ? 8 : 100); + + buf = kmalloc(6, GFP_KERNEL); + if(!buf) { + dbg ("Cannot allocate memory for buffer"); + ret = -ENOMEM; + goto out2; + } + + if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, + 0x00B0, 0, 0, buf)) < 0) + goto out3; + + msleep(5); + if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0x0001, 0, 0, buf)) < 0) { + dbg("Select PHY #1 failed: %d", ret); + goto out3; + } + + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD, 0, 0, buf)) < 0) { + dbg("Failed to power down internal PHY: %d", ret); + goto out3; + } + + msleep(150); + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, buf)) < 0) { + dbg("Failed to perform software reset: %d", ret); + goto out3; + } + + msleep(150); + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) { + dbg("Failed to set Internal/External PHY reset control: %d", ret); + goto out3; + } + + msleep(150); + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0000, 0, 0, + buf)) < 0) { + dbg("Failed to reset RX_CTL: %d", ret); + goto out3; + } + + /* Get the MAC address */ + memset(buf, 0, ETH_ALEN); + if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)) < 0) { + dbg("Failed to read MAC address: %d", ret); + goto out3; + } + memcpy(dev->net->dev_addr, buf, ETH_ALEN); + + if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, buf)) < 0) { + dbg("Enabling software MII failed: %d", ret); + goto out3; + } + + if (((ret = + ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, 0x0010, 2, 2, buf)) < 0) + || (*((u16 *)buf) != 0x003b)) { + dbg("Read PHY register 2 must be 0x3b00: %d", ret); + goto out3; + } + + /* Initialize MII structure */ + dev->mii.dev = dev->net; + dev->mii.mdio_read = ax8817x_mdio_read; + dev->mii.mdio_write = ax8817x_mdio_write; + dev->mii.phy_id_mask = 0xff; + dev->mii.reg_num_mask = 0xff; + + /* Get the PHY id */ + if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) { + dbg("Error reading PHY ID: %02x", ret); + goto out3; + } else if (ret < 2) { + /* this should always return 2 bytes */ + dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", + ret); + ret = -EIO; + goto out3; + } + dev->mii.phy_id = *((u8 *)buf + 1); + + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL, 0, 0, buf)) < 0) { + dbg("Set external PHY reset pin level: %d", ret); + goto out3; + } + msleep(150); + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) { + dbg("Set Internal/External PHY reset control: %d", ret); + goto out3; + } + msleep(150); + + + dev->net->set_multicast_list = ax8817x_set_multicast; + dev->net->ethtool_ops = &ax88772_ethtool_ops; + + ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, + cpu_to_le16(BMCR_RESET)); + ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, + cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA)); + mii_nway_restart(&dev->mii); + + if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0x0336, 0, 0, buf)) < 0) { + dbg("Write medium mode register: %d", ret); + goto out3; + } + + if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,AX88772_IPG2_DEFAULT, 0, buf)) < 0) { + dbg("Write IPG,IPG1,IPG2 failed: %d", ret); + goto out3; + } + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) { + dbg("Failed to set hardware MII: %02x", ret); + goto out3; + } + + /* Set RX_CTL to default values with 2k buffer, and enable cactus */ + if ((ret = + ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0, + buf)) < 0) { + dbg("Reset RX_CTL failed: %d", ret); + goto out3; + } + + if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) { + dbg("Failed to submit interrupt URB: %02x", ret); + goto out3; + } + + kfree(buf); + + return 0; + +out3: + kfree(buf); +out2: + kfree(data->int_buf); +out1: + usb_free_urb(data->int_urb); + + return ret; +} + +static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + u32 *header; + char *packet; + struct sk_buff *ax_skb; + u16 size; + + header = (u32 *) skb->data; + le32_to_cpus(header); + packet = (char *)(header + 1); + + skb_pull(skb, 4); + + while (skb->len > 0) { + if ((short)(*header & 0x0000ffff) != + ~((short)((*header & 0xffff0000) >> 16))) { + devdbg(dev,"header length data is error"); + } + /* get the packet length */ + size = (u16) (*header & 0x0000ffff); + + if ((skb->len) - ((size + 1) & 0xfffe) == 0) + return 2; + if (size > ETH_FRAME_LEN) { + devdbg(dev,"invalid rx length %d", size); + return 0; + } + ax_skb = skb_clone(skb, GFP_ATOMIC); + if (ax_skb) { + ax_skb->len = size; + ax_skb->data = packet; + ax_skb->tail = packet + size; + skb_return(dev, ax_skb); + } else { + return 0; + } + + skb_pull(skb, (size + 1) & 0xfffe); + + if (skb->len == 0) + break; + + header = (u32 *) skb->data; + le32_to_cpus(header); + packet = (char *)(header + 1); + skb_pull(skb, 4); + } + + if (skb->len < 0) { + devdbg(dev,"invalid rx length %d", skb->len); + return 0; + } + return 1; +} + +static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb, + int flags) +{ + int padlen; + int headroom = skb_headroom(skb); + int tailroom = skb_tailroom(skb); + u32 *packet_len; + u32 *padbytes_ptr; + + padlen = ((skb->len + 4) % 512) ? 0 : 4; + + if ((!skb_cloned(skb)) + && ((headroom + tailroom) >= (4 + padlen))) { + if ((headroom < 4) || (tailroom < padlen)) { + skb->data = memmove(skb->head + 4, skb->data, skb->len); + skb->tail = skb->data + skb->len; + } + } else { + struct sk_buff *skb2; + skb2 = skb_copy_expand(skb, 4, padlen, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + packet_len = (u32 *) skb_push(skb, 4); + + packet_len = (u32 *) skb->data; + *packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); + + if ((skb->len % 512) == 0) { + padbytes_ptr = (u32 *) skb->tail; + *padbytes_ptr = 0xffff0000; + skb_put(skb, padlen); + } + return skb; +} + static const struct driver_info ax8817x_info = { .description = "ASIX AX8817x USB 2.0 Ethernet", .bind = ax8817x_bind, @@ -864,6 +1190,16 @@ .data = 0x001f1d1f, }; +static const struct driver_info ax88772_info = { + .description = "ASIX AX88772 USB 2.0 Ethernet", + .bind = ax88772_bind, + .unbind = ax8817x_unbind, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88772_rx_fixup, + .tx_fixup = ax88772_tx_fixup, + .data = 0x00130103, +}; + #endif /* CONFIG_USB_AX8817X */ @@ -911,45 +1247,14 @@ #ifdef NEED_GENERIC_CDC -/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ -struct header_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u16 bcdCDC; -} __attribute__ ((packed)); - -/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ -struct union_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 bMasterInterface0; - u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ -struct ether_desc { - u8 bLength; - u8 bDescriptorType; - u8 bDescriptorSubType; - - u8 iMACAddress; - u32 bmEthernetStatistics; - __le16 wMaxSegmentSize; - __le16 wNumberMCFilters; - u8 bNumberPowerFilters; -} __attribute__ ((packed)); +#include struct cdc_state { - struct header_desc *header; - struct union_desc *u; - struct ether_desc *ether; - struct usb_interface *control; - struct usb_interface *data; + struct usb_cdc_header_desc *header; + struct usb_cdc_union_desc *u; + struct usb_cdc_ether_desc *ether; + struct usb_interface *control; + struct usb_interface *data; }; static struct usb_driver usbnet_driver; @@ -1004,7 +1309,7 @@ * CDC Ethernet achieves with a simple descriptor. */ switch (buf [2]) { - case 0x00: /* Header, mostly useless */ + case USB_CDC_HEADER_TYPE: if (info->header) { dev_dbg (&intf->dev, "extra CDC header\n"); goto bad_desc; @@ -1016,7 +1321,7 @@ goto bad_desc; } break; - case 0x06: /* Union (groups interfaces) */ + case USB_CDC_UNION_TYPE: if (info->u) { dev_dbg (&intf->dev, "extra CDC union\n"); goto bad_desc; @@ -1065,7 +1370,7 @@ goto bad_desc; } break; - case 0x0F: /* Ethernet Networking */ + case USB_CDC_ETHERNET_TYPE: if (info->ether) { dev_dbg (&intf->dev, "extra CDC ether\n"); goto bad_desc; @@ -1169,7 +1474,7 @@ } static inline int -get_ethernet_addr (struct usbnet *dev, struct ether_desc *e) +get_ethernet_addr (struct usbnet *dev, struct usb_cdc_ether_desc *e) { int tmp, i; unsigned char buf [13]; @@ -1602,9 +1907,9 @@ */ struct nc_header { // packed: - u16 hdr_len; // sizeof nc_header (LE, all) - u16 packet_len; // payload size (including ethhdr) - u16 packet_id; // detects dropped packets + __le16 hdr_len; // sizeof nc_header (LE, all) + __le16 packet_len; // payload size (including ethhdr) + __le16 packet_id; // detects dropped packets #define MIN_HEADER 6 // all else is optional, and must start with: @@ -1615,7 +1920,7 @@ #define PAD_BYTE ((unsigned char)0xAC) struct nc_trailer { - u16 packet_id; + __le16 packet_id; } __attribute__((__packed__)); // packets may use FLAG_FRAMING_NC and optional pad @@ -1973,6 +2278,7 @@ { struct nc_header *header; struct nc_trailer *trailer; + u16 hdr_len, packet_len; if (!(skb->len & 0x01) || MIN_FRAMED > skb->len @@ -1986,50 +2292,50 @@ } header = (struct nc_header *) skb->data; - le16_to_cpus (&header->hdr_len); - le16_to_cpus (&header->packet_len); - if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) { + hdr_len = le16_to_cpup (&header->hdr_len); + packet_len = le16_to_cpup (&header->packet_len); + if (FRAMED_SIZE (packet_len) > MAX_PACKET) { dev->stats.rx_frame_errors++; - dbg ("packet too big, %d", header->packet_len); + dbg ("packet too big, %d", packet_len); nc_ensure_sync (dev); return 0; - } else if (header->hdr_len < MIN_HEADER) { + } else if (hdr_len < MIN_HEADER) { dev->stats.rx_frame_errors++; - dbg ("header too short, %d", header->hdr_len); + dbg ("header too short, %d", hdr_len); nc_ensure_sync (dev); return 0; - } else if (header->hdr_len > MIN_HEADER) { + } else if (hdr_len > MIN_HEADER) { // out of band data for us? - dbg ("header OOB, %d bytes", - header->hdr_len - MIN_HEADER); + dbg ("header OOB, %d bytes", hdr_len - MIN_HEADER); nc_ensure_sync (dev); // switch (vendor/product ids) { ... } } - skb_pull (skb, header->hdr_len); + skb_pull (skb, hdr_len); trailer = (struct nc_trailer *) (skb->data + skb->len - sizeof *trailer); skb_trim (skb, skb->len - sizeof *trailer); - if ((header->packet_len & 0x01) == 0) { - if (skb->data [header->packet_len] != PAD_BYTE) { + if ((packet_len & 0x01) == 0) { + if (skb->data [packet_len] != PAD_BYTE) { dev->stats.rx_frame_errors++; dbg ("bad pad"); return 0; } skb_trim (skb, skb->len - 1); } - if (skb->len != header->packet_len) { + if (skb->len != packet_len) { dev->stats.rx_frame_errors++; dbg ("bad packet len %d (expected %d)", - skb->len, header->packet_len); + skb->len, packet_len); nc_ensure_sync (dev); return 0; } if (header->packet_id != get_unaligned (&trailer->packet_id)) { dev->stats.rx_fifo_errors++; dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x", - header->packet_id, trailer->packet_id); + le16_to_cpu (header->packet_id), + le16_to_cpu (trailer->packet_id)); return 0; } #if 0 @@ -2402,6 +2708,11 @@ size = RNDIS_MAX_TRANSFER; else #endif +#ifdef CONFIG_USB_AX8817X + if (dev->driver_info->flags & FLAG_FRAMING_AX) + size = 2048; + else +#endif size = (sizeof (struct ethhdr) + dev->net->mtu); if ((skb = alloc_skb (size, flags)) == NULL) { @@ -2677,6 +2988,8 @@ framing = "Zaurus"; else if (dev->driver_info->flags & FLAG_FRAMING_RN) framing = "RNDIS"; + else if (dev->driver_info->flags & FLAG_FRAMING_AX) + framing = "ASIX"; else framing = "simple"; @@ -3197,6 +3510,32 @@ return status; } +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +static int usbnet_suspend (struct usb_interface *intf, u32 state) +{ + struct usbnet *dev = usb_get_intfdata(intf); + + netif_device_detach (dev->net); + return 0; +} + +static int usbnet_resume (struct usb_interface *intf) +{ + struct usbnet *dev = usb_get_intfdata(intf); + + netif_device_attach (dev->net); + return 0; +} + +#else /* !CONFIG_PM */ + +#define usbnet_suspend NULL +#define usbnet_resume NULL + +#endif /* CONFIG_PM */ /*-------------------------------------------------------------------------*/ @@ -3290,6 +3629,10 @@ // goodway corp usb gwusb2e USB_DEVICE (0x1631, 0x6200), .driver_info = (unsigned long) &ax8817x_info, +}, { + // ASIX AX88772 10/100 + USB_DEVICE (0x0b95, 0x7720), + .driver_info = (unsigned long) &ax88772_info, }, #endif @@ -3374,6 +3717,7 @@ .driver_info = (unsigned long) &blob_info, }, { // Linux Ethernet/RNDIS gadget on pxa210/25x/26x + // e.g. Gumstix, current OpenZaurus, ... USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203), .driver_info = (unsigned long) &linuxdev_info, }, @@ -3386,72 +3730,64 @@ * * PXA-2xx based models are also lying-about-cdc. * + * NOTE: OpenZaurus versions with 2.6 kernels won't use these entries, + * unlike the older ones with 2.4 "embedix" kernels. + * * NOTE: These entries do double-duty, serving as blacklist entries * whenever Zaurus support isn't enabled, but CDC Ethernet is. */ +#define ZAURUS_MASTER_INTERFACE \ + .bInterfaceClass = USB_CLASS_COMM, \ + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, \ + .bInterfaceProtocol = USB_CDC_PROTO_NONE { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8004, - /* match the master interface */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_STRONGARM_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8005, /* A-300 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8006, /* B-500/SL-5600 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x8007, /* C-700 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9031, /* C-750 C-760 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9032, /* SL-6000 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x04DD, .idProduct = 0x9050, /* C-860 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = ZAURUS_PXA_INFO, }, @@ -3463,9 +3799,7 @@ | USB_DEVICE_ID_MATCH_DEVICE, .idVendor = 0x07B4, .idProduct = 0x0F02, /* R-1000 */ - .bInterfaceClass = USB_CLASS_COMM, - .bInterfaceSubClass = 6 /* Ethernet model */, - .bInterfaceProtocol = 0x00, + ZAURUS_MASTER_INTERFACE, .driver_info = OLYMPUS_MXL_INFO, }, #endif @@ -3480,7 +3814,8 @@ * NOTE: this match must come AFTER entries working around * bugs/quirks in a given product (like Zaurus, above). */ - USB_INTERFACE_INFO (USB_CLASS_COMM, 6 /* Ethernet model */, 0), + USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, + USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, }, #endif @@ -3495,6 +3830,8 @@ .id_table = products, .probe = usbnet_probe, .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, }; /* Default ethtool_ops assigned. Devices can override in their bind() routine */ diff -Nru a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c --- a/drivers/usb/serial/cypress_m8.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/serial/cypress_m8.c 2005-02-04 22:22:37 -08:00 @@ -57,9 +57,10 @@ #include #include #include -#include #include #include +#include +#include #include "usb-serial.h" #include "cypress_m8.h" @@ -1013,8 +1014,7 @@ cypress_serial_control(port, baud_mask, data_bits, stop_bits, parity_enable, parity_type, 0, CYPRESS_SET_CONFIG); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(50*HZ/1000); /* give some time between change and read (50ms) */ + msleep(50); /* give some time between change and read (50ms) */ /* we perform a CYPRESS_GET_CONFIG so that the current settings are filled into the private structure * this should confirm that all is working if it returns what we just set */ diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c --- a/drivers/usb/serial/ftdi_sio.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/serial/ftdi_sio.c 2005-02-04 22:22:37 -08:00 @@ -296,6 +296,7 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0, 0x3ff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0, 0x3ff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0, 0x3ff) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_MINI_IOBOARD_PID) }, @@ -381,6 +382,7 @@ { USB_DEVICE_VER(FTDI_VID, FTDI_IRTRANS_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_PID, 0x400, 0xffff) }, + { USB_DEVICE_VER(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_RELAIS_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_XF_632_PID, 0x400, 0xffff) }, @@ -515,6 +517,7 @@ { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_ALT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, { USB_DEVICE(INTERBIOMETRICS_VID, INTERBIOMETRICS_IOBOARD_PID) }, diff -Nru a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h --- a/drivers/usb/serial/ftdi_sio.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/serial/ftdi_sio.h 2005-02-04 22:22:37 -08:00 @@ -26,6 +26,7 @@ #define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */ #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ +#define FTDI_8U232AM_ALT_ALT_PID 0xf3c0 /* FTDI's second alternate PID for above */ #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ #define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ diff -Nru a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c --- a/drivers/usb/serial/io_edgeport.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/serial/io_edgeport.c 2005-02-04 22:22:37 -08:00 @@ -261,6 +261,7 @@ #include #include #include +#include #include #include #include "usb-serial.h" @@ -971,7 +972,7 @@ /* we have completed the command */ edge_port->commandPending = FALSE; - wake_up_interruptible(&edge_port->wait_command); + wake_up(&edge_port->wait_command); } @@ -991,7 +992,6 @@ struct usb_serial *serial; struct edgeport_serial *edge_serial; int response; - int timeout; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1073,10 +1073,7 @@ } /* now wait for the port to be completely opened */ - timeout = OPEN_TIMEOUT; - while (timeout && edge_port->openPending == TRUE) { - timeout = interruptible_sleep_on_timeout (&edge_port->wait_open, timeout); - } + wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT); if (edge_port->open == FALSE) { /* open timed out */ @@ -1128,9 +1125,10 @@ ************************************************************************/ static void block_until_chase_response(struct edgeport_port *edge_port) { + DEFINE_WAIT(wait); __u16 lastCredits; int timeout = 1*HZ; - int wait = 10; + int loop = 10; while (1) { // Save Last credits @@ -1148,12 +1146,14 @@ } // Block the thread for a while - interruptible_sleep_on_timeout (&edge_port->wait_chase, timeout); + prepare_to_wait(&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + finish_wait(&edge_port->wait_chase, &wait); if (lastCredits == edge_port->txCredits) { // No activity.. count down. - wait--; - if (wait == 0) { + loop--; + if (loop == 0) { edge_port->chaseResponsePending = FALSE; dbg("%s - Chase TIMEOUT", __FUNCTION__); return; @@ -1161,7 +1161,7 @@ } else { // Reset timout value back to 10 seconds dbg("%s - Last %d, Current %d", __FUNCTION__, lastCredits, edge_port->txCredits); - wait = 10; + loop = 10; } } } @@ -1179,10 +1179,11 @@ ************************************************************************/ static void block_until_tx_empty (struct edgeport_port *edge_port) { + DEFINE_WAIT(wait); struct TxFifo *fifo = &edge_port->txfifo; __u32 lastCount; int timeout = HZ/10; - int wait = 30; + int loop = 30; while (1) { // Save Last count @@ -1195,20 +1196,22 @@ } // Block the thread for a while - interruptible_sleep_on_timeout (&edge_port->wait_chase, timeout); + prepare_to_wait (&edge_port->wait_chase, &wait, TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + finish_wait(&edge_port->wait_chase, &wait); dbg("%s wait", __FUNCTION__); if (lastCount == fifo->count) { // No activity.. count down. - wait--; - if (wait == 0) { + loop--; + if (loop == 0) { dbg("%s - TIMEOUT", __FUNCTION__); return; } } else { // Reset timout value back to seconds - wait = 30; + loop = 30; } } } @@ -1836,12 +1839,12 @@ *****************************************************************************/ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg) { + DEFINE_WAIT(wait); struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct async_icount cnow; struct async_icount cprev; struct serial_icounter_struct icount; - dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd); switch (cmd) { @@ -1868,7 +1871,9 @@ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number); cprev = edge_port->icount; while (1) { - interruptible_sleep_on(&edge_port->delta_msr_wait); + prepare_to_wait(&edge_port->delta_msr_wait, &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(&edge_port->delta_msr_wait, &wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -2108,7 +2113,7 @@ // We could choose to do something else when Byte3 says Timeout on Chase from Edgeport, // like wait longer in block_until_chase_response, but for now we don't. edge_port->chaseResponsePending = FALSE; - wake_up_interruptible (&edge_port->wait_chase); + wake_up (&edge_port->wait_chase); return; case IOSP_EXT_STATUS_RX_CHECK_RSP: @@ -2131,7 +2136,7 @@ /* we have completed the open */ edge_port->openPending = FALSE; edge_port->open = TRUE; - wake_up_interruptible(&edge_port->wait_open); + wake_up(&edge_port->wait_open); return; } @@ -2500,9 +2505,7 @@ // wait for command to finish timeout = COMMAND_TIMEOUT; #if 0 - while (timeout && edge_port->commandPending == TRUE) { - timeout = interruptible_sleep_on_timeout (&edge_port->wait_command, timeout); - } + wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE)); if (edge_port->commandPending == TRUE) { /* command timed out */ diff -Nru a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig --- a/drivers/usb/storage/Kconfig 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/Kconfig 2005-02-04 22:22:37 -08:00 @@ -89,12 +89,26 @@ Say Y here to support the Microtech/ZiO! CompactFlash reader. There is a web page at . -config USB_STORAGE_HP8200e - bool "HP CD-Writer 82xx support (EXPERIMENTAL)" +config USB_STORAGE_USBAT + bool "USBAT/USBAT02-based storage support (EXPERIMENTAL)" depends on USB_STORAGE && EXPERIMENTAL help - Say Y here to include additional code to support Hewlett-Packard - 8200e/8210e/8230e CD-Writer Plus drives. + Say Y here to include additional code to support storage devices + based on the SCM/Shuttle USBAT/USBAT02 processors. + + Devices reported to work with this driver include: + - CompactFlash reader included with Kodak DC3800 camera + - Dane-Elec Zmate CompactFlash reader + - Delkin Efilm reader2 + - HP 8200e/8210e/8230e CD-Writer Plus drives + - I-JAM JS-50U + - Jessops CompactFlash JESDCFRU BLACK + - Kingston Technology PCREAD-USB/CF + - Maxell UA4 CompactFlash reader + - Memorex UCF-100 + - Microtech ZiO! ICS-45 CF2 + - RCA LYRA MP3 portable + - Sandisk ImageMate SDDR-05b config USB_STORAGE_SDDR09 bool "SanDisk SDDR-09 (and other SmartMedia) support (EXPERIMENTAL)" diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile --- a/drivers/usb/storage/Makefile 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/Makefile 2005-02-04 22:22:37 -08:00 @@ -10,7 +10,7 @@ obj-$(CONFIG_USB_STORAGE) += usb-storage.o usb-storage-obj-$(CONFIG_USB_STORAGE_DEBUG) += debug.o -usb-storage-obj-$(CONFIG_USB_STORAGE_HP8200e) += shuttle_usbat.o +usb-storage-obj-$(CONFIG_USB_STORAGE_USBAT) += shuttle_usbat.o usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR09) += sddr09.o usb-storage-obj-$(CONFIG_USB_STORAGE_SDDR55) += sddr55.o usb-storage-obj-$(CONFIG_USB_STORAGE_FREECOM) += freecom.o diff -Nru a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c --- a/drivers/usb/storage/protocol.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/protocol.c 2005-02-04 22:22:37 -08:00 @@ -54,39 +54,6 @@ #include "transport.h" /*********************************************************************** - * Helper routines - ***********************************************************************/ - -/* - * Fix-up the return data from a READ CAPACITY command. My Feiya reader - * returns a value that is 1 too large. - */ -static void fix_read_capacity(struct scsi_cmnd *srb) -{ - unsigned int index, offset; - __be32 c; - unsigned long capacity; - - /* verify that it's a READ CAPACITY command */ - if (srb->cmnd[0] != READ_CAPACITY) - return; - - index = offset = 0; - if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, - &index, &offset, FROM_XFER_BUF) != 4) - return; - - capacity = be32_to_cpu(c); - US_DEBUGP("US: Fixing capacity: from %ld to %ld\n", - capacity+1, capacity); - c = cpu_to_be32(capacity - 1); - - index = offset = 0; - usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb, - &index, &offset, TO_XFER_BUF); -} - -/*********************************************************************** * Protocol routines ***********************************************************************/ @@ -174,12 +141,6 @@ { /* send the command to the transport layer */ usb_stor_invoke_transport(srb, us); - - if (srb->result == SAM_STAT_GOOD) { - /* Fix the READ CAPACITY result if necessary */ - if (us->flags & US_FL_FIX_CAPACITY) - fix_read_capacity(srb); - } } /*********************************************************************** diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/scsiglue.c 2005-02-04 22:22:37 -08:00 @@ -149,6 +149,11 @@ sdev->skip_ms_page_3f = 1; #endif + /* Some disks return the total number blocks in response + * to READ CAPACITY rather than the highest block number. + * If this device makes that mistake, tell the sd driver. */ + if (us->flags & US_FL_FIX_CAPACITY) + sdev->fix_capacity = 1; } else { /* Non-disk-type devices don't need to blacklist any pages @@ -156,6 +161,11 @@ * But they do need to use MODE SENSE(10). */ sdev->use_10_for_ms = 1; } + + /* Some devices choke when they receive a PREVENT-ALLOW MEDIUM + * REMOVAL command, so suppress those commands. */ + if (us->flags & US_FL_NOT_LOCKABLE) + sdev->lockable = 0; /* this is to satisfy the compiler, tho I don't think the * return code is ever checked anywhere. */ diff -Nru a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c --- a/drivers/usb/storage/shuttle_usbat.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/shuttle_usbat.c 2005-02-04 22:22:37 -08:00 @@ -4,10 +4,14 @@ * * Current development and maintenance by: * (c) 2000, 2001 Robert Baruch (autophile@starband.net) + * (c) 2004, 2005 Daniel Drake * * Developed with the assistance of: * (c) 2002 Alan Stern * + * Flash support based on earlier work by: + * (c) 2002 Thomas Kreiling + * * Many originally ATAPI devices were slightly modified to meet the USB * market by using some kind of translation from ATAPI to USB on the host, * and the peripheral would translate from USB back to ATAPI. @@ -21,8 +25,8 @@ * as well. This driver is only guaranteed to work with the ATAPI * translation. * - * The only peripheral that I know of (as of 27 Mar 2001) that uses this - * device is the Hewlett-Packard 8200e/8210e/8230e CD-Writer Plus. + * See the Kconfig help text for a list of devices known to be supported by + * this driver. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -59,44 +63,149 @@ int transferred = 0; +/* + * Convenience function to produce an ATAPI read/write sectors command + * Use cmd=0x20 for read, cmd=0x30 for write + */ +static void usbat_pack_atapi_sector_cmd(unsigned char *buf, + unsigned char thistime, + u32 sector, unsigned char cmd) +{ + buf[0] = 0; + buf[1] = thistime; + buf[2] = sector & 0xFF; + buf[3] = (sector >> 8) & 0xFF; + buf[4] = (sector >> 16) & 0xFF; + buf[5] = 0xE0 | ((sector >> 24) & 0x0F); + buf[6] = cmd; +} + +/* + * Convenience function to get the device type (flash or hp8200) + */ +static int usbat_get_device_type(struct us_data *us) +{ + return ((struct usbat_info*)us->extra)->devicetype; +} + +/* + * Read a register from the device + */ static int usbat_read(struct us_data *us, unsigned char access, unsigned char reg, unsigned char *content) { - int result; - - result = usb_stor_ctrl_transfer(us, + return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, - access, + access | USBAT_CMD_READ_REG, 0xC0, (u16)reg, 0, content, 1); - - return result; } +/* + * Write to a register on the device + */ static int usbat_write(struct us_data *us, unsigned char access, unsigned char reg, unsigned char content) { - int result; - - result = usb_stor_ctrl_transfer(us, + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, - access|0x01, + access | USBAT_CMD_WRITE_REG, 0x40, short_pack(reg, content), 0, NULL, 0); +} - return result; +/* + * Convenience function to perform a bulk read + */ +static int usbat_bulk_read(struct us_data *us, + unsigned char *data, + unsigned int len) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + US_DEBUGP("usbat_bulk_read: len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, len, NULL); } +/* + * Convenience function to perform a bulk write + */ +static int usbat_bulk_write(struct us_data *us, + unsigned char *data, + unsigned int len) +{ + if (len == 0) + return USB_STOR_XFER_GOOD; + + US_DEBUGP("usbat_bulk_write: len = %d\n", len); + return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, data, len, NULL); +} + +/* + * Some USBAT-specific commands can only be executed over a command transport + * This transport allows one (len=8) or two (len=16) vendor-specific commands + * to be executed. + */ +static int usbat_execute_command(struct us_data *us, + unsigned char *commands, + unsigned int len) +{ + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + USBAT_CMD_EXEC_CMD, 0x40, 0, 0, + commands, len); +} + +/* + * Read the status register + */ +static int usbat_get_status(struct us_data *us, unsigned char *status) +{ + int rc; + rc = usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status); + + US_DEBUGP("usbat_get_status: 0x%02X\n", (unsigned short) (*status)); + return rc; +} + +/* + * Check the device status + */ +static int usbat_check_status(struct us_data *us) +{ + unsigned char *reply = us->iobuf; + int rc; + + if (!us) + return USB_STOR_TRANSPORT_ERROR; + + rc = usbat_get_status(us, reply); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + if (*reply & 0x01 && *reply != 0x51) // error/check condition (0x51 is ok) + return USB_STOR_TRANSPORT_FAILED; + + if (*reply & 0x20) // device fault + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Stores critical information in internal registers in prepartion for the execution + * of a conditional usbat_read_blocks or usbat_write_blocks call. + */ static int usbat_set_shuttle_features(struct us_data *us, unsigned char external_trigger, unsigned char epp_control, @@ -105,76 +214,44 @@ unsigned char subcountH, unsigned char subcountL) { - int result; unsigned char *command = us->iobuf; command[0] = 0x40; - command[1] = 0x81; + command[1] = USBAT_CMD_SET_FEAT; + + // The only bit relevant to ATA access is bit 6 + // which defines 8 bit data access (set) or 16 bit (unset) command[2] = epp_control; + + // If FCQ is set in the qualifier (defined in R/W cmd), then bits U0, U1, + // ET1 and ET2 define an external event to be checked for on event of a + // _read_blocks or _write_blocks operation. The read/write will not take + // place unless the defined trigger signal is active. command[3] = external_trigger; + + // The resultant byte of the mask operation (see mask_byte) is compared for + // equivalence with this test pattern. If equal, the read/write will take + // place. command[4] = test_pattern; + + // This value is logically ANDed with the status register field specified + // in the read/write command. command[5] = mask_byte; + + // If ALQ is set in the qualifier, this field contains the address of the + // registers where the byte count should be read for transferring the data. + // If ALQ is not set, then this field contains the number of bytes to be + // transferred. command[6] = subcountL; command[7] = subcountH; - result = usb_stor_ctrl_transfer(us, - us->send_ctrl_pipe, - 0x80, - 0x40, - 0, - 0, - command, - 8); - - return result; -} - -static int usbat_read_block(struct us_data *us, - unsigned char access, - unsigned char reg, - unsigned char *content, - unsigned short len, - int use_sg) -{ - int result; - unsigned char *command = us->iobuf; - - if (!len) - return USB_STOR_TRANSPORT_GOOD; - - command[0] = 0xC0; - command[1] = access | 0x02; - command[2] = reg; - command[3] = 0; - command[4] = 0; - command[5] = 0; - command[6] = LSB_of(len); - command[7] = MSB_of(len); - - result = usb_stor_ctrl_transfer(us, - us->send_ctrl_pipe, - 0x80, - 0x40, - 0, - 0, - command, - 8); - - if (result != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; - - result = usb_stor_bulk_transfer_sg(us, us->recv_bulk_pipe, - content, len, use_sg, NULL); - - return (result == USB_STOR_XFER_GOOD ? - USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + return usbat_execute_command(us, command, 8); } /* * Block, waiting for an ATA device to become not busy or to report * an error condition. */ - static int usbat_wait_not_busy(struct us_data *us, int minutes) { int i; @@ -189,7 +266,7 @@ for (i=0; i<1200+minutes*60; i++) { - result = usbat_read(us, USBAT_ATA, 0x17, status); + result = usbat_get_status(us, status); if (result!=USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -220,12 +297,45 @@ return USB_STOR_TRANSPORT_FAILED; } +/* + * Read block data from the data register + */ +static int usbat_read_block(struct us_data *us, + unsigned char *content, + unsigned short len) +{ + int result; + unsigned char *command = us->iobuf; + + if (!len) + return USB_STOR_TRANSPORT_GOOD; + + command[0] = 0xC0; + command[1] = USBAT_ATA | USBAT_CMD_READ_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = 0; + command[4] = 0; + command[5] = 0; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + result = usbat_bulk_read(us, content, len); + return (result == USB_STOR_XFER_GOOD ? + USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); +} + +/* + * Write block data via the data register + */ static int usbat_write_block(struct us_data *us, - unsigned char access, - unsigned char reg, + unsigned char access, unsigned char *content, unsigned short len, - int use_sg, int minutes) + int minutes) { int result; unsigned char *command = us->iobuf; @@ -234,57 +344,48 @@ return USB_STOR_TRANSPORT_GOOD; command[0] = 0x40; - command[1] = access | 0x03; - command[2] = reg; + command[1] = access | USBAT_CMD_WRITE_BLOCK; + command[2] = USBAT_ATA_DATA; command[3] = 0; command[4] = 0; command[5] = 0; command[6] = LSB_of(len); command[7] = MSB_of(len); - result = usb_stor_ctrl_transfer(us, - us->send_ctrl_pipe, - 0x80, - 0x40, - 0, - 0, - command, - 8); + result = usbat_execute_command(us, command, 8); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - result = usb_stor_bulk_transfer_sg(us, us->send_bulk_pipe, - content, len, use_sg, NULL); - + result = usbat_bulk_write(us, content, len); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; return usbat_wait_not_busy(us, minutes); } -static int usbat_rw_block_test(struct us_data *us, - unsigned char access, - unsigned char *registers, - unsigned char *data_out, - unsigned short num_registers, - unsigned char data_reg, - unsigned char status_reg, - unsigned char timeout, - unsigned char qualifier, - int direction, - unsigned char *content, - unsigned short len, - int use_sg, - int minutes) +/* + * Process read and write requests + */ +static int usbat_hp8200e_rw_block_test(struct us_data *us, + unsigned char access, + unsigned char *registers, + unsigned char *data_out, + unsigned short num_registers, + unsigned char data_reg, + unsigned char status_reg, + unsigned char timeout, + unsigned char qualifier, + int direction, + unsigned char *content, + unsigned short len, + int use_sg, + int minutes) { int result; unsigned int pipe = (direction == DMA_FROM_DEVICE) ? us->recv_bulk_pipe : us->send_bulk_pipe; - // Not really sure the 0x07, 0x17, 0xfc, 0xe7 is necessary here, - // but that's what came out of the trace every single time. - unsigned char *command = us->iobuf; int i, j; int cmdlen; @@ -308,8 +409,11 @@ if (i==0) { cmdlen = 16; + // Write to multiple registers + // Not really sure the 0x07, 0x17, 0xfc, 0xe7 is necessary here, + // but that's what came out of the trace every single time. command[0] = 0x40; - command[1] = access | 0x07; + command[1] = access | USBAT_CMD_WRITE_REGS; command[2] = 0x07; command[3] = 0x17; command[4] = 0xFC; @@ -319,9 +423,11 @@ } else cmdlen = 8; + // Conditionally read or write blocks command[cmdlen-8] = (direction==DMA_TO_DEVICE ? 0x40 : 0xC0); command[cmdlen-7] = access | - (direction==DMA_TO_DEVICE ? 0x05 : 0x04); + (direction==DMA_TO_DEVICE ? + USBAT_CMD_COND_WRITE_BLOCK : USBAT_CMD_COND_READ_BLOCK); command[cmdlen-6] = data_reg; command[cmdlen-5] = status_reg; command[cmdlen-4] = timeout; @@ -329,14 +435,7 @@ command[cmdlen-2] = LSB_of(len); command[cmdlen-1] = MSB_of(len); - result = usb_stor_ctrl_transfer(us, - us->send_ctrl_pipe, - 0x80, - 0x40, - 0, - 0, - command, - cmdlen); + result = usbat_execute_command(us, command, cmdlen); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -348,10 +447,7 @@ data[1+(j<<1)] = data_out[j]; } - result = usb_stor_bulk_transfer_buf(us, - us->send_bulk_pipe, - data, num_registers*2, NULL); - + result = usbat_bulk_write(us, data, num_registers*2); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -403,7 +499,8 @@ */ result = usbat_read(us, USBAT_ATA, - direction==DMA_TO_DEVICE ? 0x17 : 0x0E, + direction==DMA_TO_DEVICE ? + USBAT_ATA_STATUS : USBAT_ATA_ALTSTATUS, status); if (result!=USB_STOR_XFER_GOOD) @@ -430,101 +527,602 @@ } /* - * Write data to multiple registers at once. Not meant for large - * transfers of data! + * Write to multiple registers: + * Allows us to write specific data to any registers. The data to be written + * gets packed in this sequence: reg0, data0, reg1, data1, ..., regN, dataN + * which gets sent through bulk out. + * Not designed for large transfers of data! */ - static int usbat_multiple_write(struct us_data *us, - unsigned char access, unsigned char *registers, unsigned char *data_out, unsigned short num_registers) { - int result; + int i, result; unsigned char *data = us->iobuf; - int i; unsigned char *command = us->iobuf; BUG_ON(num_registers > US_IOBUF_SIZE/2); + // Write to multiple registers, ATA access command[0] = 0x40; - command[1] = access | 0x07; + command[1] = USBAT_ATA | USBAT_CMD_WRITE_REGS; + + // No relevance command[2] = 0; command[3] = 0; command[4] = 0; command[5] = 0; + + // Number of bytes to be transferred (incl. addresses and data) command[6] = LSB_of(num_registers*2); command[7] = MSB_of(num_registers*2); - result = usb_stor_ctrl_transfer(us, - us->send_ctrl_pipe, - 0x80, - 0x40, - 0, - 0, - command, - 8); - + // The setup command + result = usbat_execute_command(us, command, 8); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; + // Create the reg/data, reg/data sequence for (i=0; isend_bulk_pipe, data, num_registers*2, NULL); - + // Send the data + result = usbat_bulk_write(us, data, num_registers*2); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - return usbat_wait_not_busy(us, 0); + if (usbat_get_device_type(us) == USBAT_DEV_HP8200) + return usbat_wait_not_busy(us, 0); + else + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Conditionally read blocks from device: + * Allows us to read blocks from a specific data register, based upon the + * condition that a status register can be successfully masked with a status + * qualifier. If this condition is not initially met, the read will wait + * up until a maximum amount of time has elapsed, as specified by timeout. + * The read will start when the condition is met, otherwise the command aborts. + * + * The qualifier defined here is not the value that is masked, it defines + * conditions for the write to take place. The actual masked qualifier (and + * other related details) are defined beforehand with _set_shuttle_features(). + */ +static int usbat_read_blocks(struct us_data *us, + unsigned char *buffer, + int len) +{ + int result; + unsigned char *command = us->iobuf; + + command[0] = 0xC0; + command[1] = USBAT_ATA | USBAT_CMD_COND_READ_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = USBAT_ATA_STATUS; + command[4] = 0xFD; // Timeout (ms); + command[5] = USBAT_QUAL_FCQ; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + // Multiple block read setup command + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + // Read the blocks we just asked for + result = usbat_bulk_read(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Conditionally write blocks to device: + * Allows us to write blocks to a specific data register, based upon the + * condition that a status register can be successfully masked with a status + * qualifier. If this condition is not initially met, the write will wait + * up until a maximum amount of time has elapsed, as specified by timeout. + * The read will start when the condition is met, otherwise the command aborts. + * + * The qualifier defined here is not the value that is masked, it defines + * conditions for the write to take place. The actual masked qualifier (and + * other related details) are defined beforehand with _set_shuttle_features(). + */ +static int usbat_write_blocks(struct us_data *us, + unsigned char *buffer, + int len) +{ + int result; + unsigned char *command = us->iobuf; + + command[0] = 0x40; + command[1] = USBAT_ATA | USBAT_CMD_COND_WRITE_BLOCK; + command[2] = USBAT_ATA_DATA; + command[3] = USBAT_ATA_STATUS; + command[4] = 0xFD; // Timeout (ms) + command[5] = USBAT_QUAL_FCQ; + command[6] = LSB_of(len); + command[7] = MSB_of(len); + + // Multiple block write setup command + result = usbat_execute_command(us, command, 8); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + // Write the data + result = usbat_bulk_write(us, buffer, len); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_FAILED; + + return USB_STOR_TRANSPORT_GOOD; } +/* + * Read the User IO register + */ static int usbat_read_user_io(struct us_data *us, unsigned char *data_flags) { int result; result = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, - 0x82, + USBAT_CMD_UIO, 0xC0, 0, 0, data_flags, - 1); + USBAT_UIO_READ); + + US_DEBUGP("usbat_read_user_io: UIO register reads %02X\n", (unsigned short) (*data_flags)); return result; } +/* + * Write to the User IO register + */ static int usbat_write_user_io(struct us_data *us, unsigned char enable_flags, unsigned char data_flags) { - int result; - - result = usb_stor_ctrl_transfer(us, + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, - 0x82, + USBAT_CMD_UIO, 0x40, short_pack(enable_flags, data_flags), 0, NULL, - 0); + USBAT_UIO_WRITE); +} + +/* + * Reset the device + * Often needed on media change. + */ +static int usbat_device_reset(struct us_data *us) +{ + int rc; + + // Reset peripheral, enable peripheral control signals + // (bring reset signal up) + rc = usbat_write_user_io(us, + USBAT_UIO_DRVRST | USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + // Enable peripheral control signals + // (bring reset signal down) + rc = usbat_write_user_io(us, + USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Enable card detect + */ +static int usbat_device_enable_cdt(struct us_data *us) +{ + int rc; + + // Enable peripheral control signals and card detect + rc = usbat_write_user_io(us, + USBAT_UIO_ACKD | USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Determine if media is present. + */ +static int usbat_flash_check_media_present(unsigned char *uio) +{ + if (*uio & USBAT_UIO_UI0) { + US_DEBUGP("usbat_flash_check_media_present: no media detected\n"); + return USBAT_FLASH_MEDIA_NONE; + } + + return USBAT_FLASH_MEDIA_CF; +} + +/* + * Determine if media has changed since last operation + */ +static int usbat_flash_check_media_changed(unsigned char *uio) +{ + if (*uio & USBAT_UIO_0) { + US_DEBUGP("usbat_flash_check_media_changed: media change detected\n"); + return USBAT_FLASH_MEDIA_CHANGED; + } + + return USBAT_FLASH_MEDIA_SAME; +} + +/* + * Check for media change / no media and handle the situation appropriately + */ +static int usbat_flash_check_media(struct us_data *us, + struct usbat_info *info) +{ + int rc; + unsigned char *uio = us->iobuf; + + rc = usbat_read_user_io(us, uio); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + // Check for media existance + rc = usbat_flash_check_media_present(uio); + if (rc == USBAT_FLASH_MEDIA_NONE) { + info->sense_key = 0x02; + info->sense_asc = 0x3A; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + // Check for media change + rc = usbat_flash_check_media_changed(uio); + if (rc == USBAT_FLASH_MEDIA_CHANGED) { + + // Reset and re-enable card detect + rc = usbat_device_reset(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + rc = usbat_device_enable_cdt(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + msleep(50); + + rc = usbat_read_user_io(us, uio); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + info->sense_key = UNIT_ATTENTION; + info->sense_asc = 0x28; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Determine whether we are controlling a flash-based reader/writer, + * or a HP8200-based CD drive. + * Sets transport functions as appropriate. + */ +static int usbat_identify_device(struct us_data *us, + struct usbat_info *info) +{ + int rc; + unsigned char status; + + if (!us || !info) + return USB_STOR_TRANSPORT_ERROR; + + rc = usbat_device_reset(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + /* + * By examining the device signature after a reset, we can identify + * whether the device supports the ATAPI packet interface. + * The flash-devices do not support this, whereas the HP CDRW's obviously + * do. + * + * This method is not ideal, but works because no other devices have been + * produced based on the USBAT/USBAT02. + * + * Section 9.1 of the ATAPI-4 spec states (amongst other things) that + * after a device reset, a Cylinder low of 0x14 indicates that the device + * does support packet commands. + */ + rc = usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, &status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUGP("usbat_identify_device: Cylinder low is %02X\n", status); + + if (status == 0x14) { + // Device is HP 8200 + US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n"); + info->devicetype = USBAT_DEV_HP8200; + } else { + // Device is a CompactFlash reader/writer + US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n"); + info->devicetype = USBAT_DEV_FLASH; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Set the transport function based on the device type + */ +int usbat_set_transport(struct us_data *us, + struct usbat_info *info) +{ + int rc; + + if (!info->devicetype) { + rc = usbat_identify_device(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) { + US_DEBUGP("usbat_set_transport: Could not identify device\n"); + return 1; + } + } + + if (usbat_get_device_type(us) == USBAT_DEV_HP8200) + us->transport = usbat_hp8200e_transport; + else if (usbat_get_device_type(us) == USBAT_DEV_FLASH) + us->transport = usbat_flash_transport; + + return 0; +} + +/* + * Read the media capacity + */ +static int usbat_flash_get_sector_count(struct us_data *us, + struct usbat_info *info) +{ + unsigned char registers[3] = { + USBAT_ATA_SECCNT, + USBAT_ATA_DEVICE, + USBAT_ATA_CMD, + }; + unsigned char command[3] = { 0x01, 0xA0, 0xEC }; + unsigned char *reply; + unsigned char status; + int rc; + + if (!us || !info) + return USB_STOR_TRANSPORT_ERROR; + + reply = kmalloc(512, GFP_NOIO); + if (!reply) + return USB_STOR_TRANSPORT_ERROR; + + // ATAPI command : IDENTIFY DEVICE + rc = usbat_multiple_write(us, registers, command, 3); + if (rc != USB_STOR_XFER_GOOD) { + US_DEBUGP("usbat_flash_get_sector_count: Gah! identify_device failed\n"); + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + // Read device status + if (usbat_get_status(us, &status) != USB_STOR_XFER_GOOD) { + rc = USB_STOR_TRANSPORT_ERROR; + goto leave; + } + + msleep(100); + + // Read the device identification data + rc = usbat_read_block(us, reply, 512); + if (rc != USB_STOR_TRANSPORT_GOOD) + goto leave; + + info->sectors = ((u32)(reply[117]) << 24) | + ((u32)(reply[116]) << 16) | + ((u32)(reply[115]) << 8) | + ((u32)(reply[114]) ); + + rc = USB_STOR_TRANSPORT_GOOD; + + leave: + kfree(reply); + return rc; +} + +/* + * Read data from device + */ +static int usbat_flash_read_data(struct us_data *us, + struct usbat_info *info, + u32 sector, + u32 sectors) +{ + unsigned char registers[7] = { + USBAT_ATA_FEATURES, + USBAT_ATA_SECCNT, + USBAT_ATA_SECNUM, + USBAT_ATA_LBA_ME, + USBAT_ATA_LBA_HI, + USBAT_ATA_DEVICE, + USBAT_ATA_STATUS, + }; + unsigned char command[7]; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_idx = 0, sg_offset = 0; + + result = usbat_flash_check_media(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Jumpshot + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + // Since we don't read more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + // ATAPI command 0x20 (READ SECTORS) + usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x20); + + // Write/execute ATAPI read command + result = usbat_multiple_write(us, registers, command, 7); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + // Read the data we just requested + result = usbat_read_blocks(us, buffer, len); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + US_DEBUGP("usbat_flash_read_data: %d bytes\n", len); + + // Store the data in the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, TO_XFER_BUF); + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); + return USB_STOR_TRANSPORT_GOOD; + +leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; +} + +/* + * Write data to device + */ +static int usbat_flash_write_data(struct us_data *us, + struct usbat_info *info, + u32 sector, + u32 sectors) +{ + unsigned char registers[7] = { + USBAT_ATA_FEATURES, + USBAT_ATA_SECCNT, + USBAT_ATA_SECNUM, + USBAT_ATA_LBA_ME, + USBAT_ATA_LBA_HI, + USBAT_ATA_DEVICE, + USBAT_ATA_STATUS, + }; + unsigned char command[7]; + unsigned char *buffer; + unsigned char thistime; + unsigned int totallen, alloclen; + int len, result; + unsigned int sg_idx = 0, sg_offset = 0; + + result = usbat_flash_check_media(us, info); + if (result != USB_STOR_TRANSPORT_GOOD) + return result; + + // we're working in LBA mode. according to the ATA spec, + // we can support up to 28-bit addressing. I don't know if Jumpshot + // supports beyond 24-bit addressing. It's kind of hard to test + // since it requires > 8GB CF card. + + if (sector > 0x0FFFFFFF) + return USB_STOR_TRANSPORT_ERROR; + + totallen = sectors * info->ssize; + + // Since we don't write more than 64 KB at a time, we have to create + // a bounce buffer and move the data a piece at a time between the + // bounce buffer and the actual transfer buffer. + + alloclen = min(totallen, 65536u); + buffer = kmalloc(alloclen, GFP_NOIO); + if (buffer == NULL) + return USB_STOR_TRANSPORT_ERROR; + + do { + // loop, never allocate or transfer more than 64k at once + // (min(128k, 255*info->ssize) is the real limit) + len = min(totallen, alloclen); + thistime = (len / info->ssize) & 0xff; + + // Get the data from the transfer buffer + usb_stor_access_xfer_buf(buffer, len, us->srb, + &sg_idx, &sg_offset, FROM_XFER_BUF); + + // ATAPI command 0x30 (WRITE SECTORS) + usbat_pack_atapi_sector_cmd(command, thistime, sector, 0x30); + + // Write/execute ATAPI write command + result = usbat_multiple_write(us, registers, command, 7); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + // Write the data + result = usbat_write_blocks(us, buffer, len); + if (result != USB_STOR_TRANSPORT_GOOD) + goto leave; + + sector += thistime; + totallen -= len; + } while (totallen > 0); + + kfree(buffer); return result; + +leave: + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; } /* * Squeeze a potentially huge (> 65535 byte) read10 command into * a little ( <= 65535 byte) ATAPI pipe */ - -static int usbat_handle_read10(struct us_data *us, - unsigned char *registers, - unsigned char *data, - struct scsi_cmnd *srb) +static int usbat_hp8200e_handle_read10(struct us_data *us, + unsigned char *registers, + unsigned char *data, + struct scsi_cmnd *srb) { int result = USB_STOR_TRANSPORT_GOOD; unsigned char *buffer; @@ -538,9 +1136,10 @@ if (srb->request_bufflen < 0x10000) { - result = usbat_rw_block_test(us, USBAT_ATA, + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, registers, data, 19, - 0x10, 0x17, 0xFD, 0x30, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), DMA_FROM_DEVICE, srb->request_buffer, srb->request_bufflen, srb->use_sg, 1); @@ -607,9 +1206,10 @@ data[7+7] = MSB_of(len / srb->transfersize); // SCSI command data[7+8] = LSB_of(len / srb->transfersize); // num sectors - result = usbat_rw_block_test(us, USBAT_ATA, + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, registers, data, 19, - 0x10, 0x17, 0xFD, 0x30, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), DMA_FROM_DEVICE, buffer, len, 0, 1); @@ -632,48 +1232,52 @@ return result; } -static int hp_8200e_select_and_test_registers(struct us_data *us) +static int usbat_select_and_test_registers(struct us_data *us) { int selector; unsigned char *status = us->iobuf; + unsigned char max_selector = 0xB0; + if (usbat_get_device_type(us) == USBAT_DEV_FLASH) + max_selector = 0xA0; // try device = master, then device = slave. - for (selector = 0xA0; selector <= 0xB0; selector += 0x10) { + for (selector = 0xA0; selector <= max_selector; selector += 0x10) { - if (usbat_write(us, USBAT_ATA, 0x16, selector) != + if (usbat_get_device_type(us) == USBAT_DEV_HP8200 && + usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x17, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_STATUS, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x16, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_DEVICE, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x14, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x15, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_write(us, USBAT_ATA, 0x14, 0x55) != + if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_ME, 0x55) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_write(us, USBAT_ATA, 0x15, 0xAA) != + if (usbat_write(us, USBAT_ATA, USBAT_ATA_LBA_HI, 0xAA) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x14, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - if (usbat_read(us, USBAT_ATA, 0x15, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; } @@ -681,125 +1285,131 @@ return USB_STOR_TRANSPORT_GOOD; } -int init_8200e(struct us_data *us) +/* + * Initialize the USBAT processor and the storage device + */ +int init_usbat(struct us_data *us) { - int result; + int rc; + struct usbat_info *info; + unsigned char subcountH = USBAT_ATA_LBA_HI; + unsigned char subcountL = USBAT_ATA_LBA_ME; unsigned char *status = us->iobuf; - // Enable peripheral control signals + us->extra = kmalloc(sizeof(struct usbat_info), GFP_NOIO); + if (!us->extra) { + US_DEBUGP("init_usbat: Gah! Can't allocate storage for usbat info struct!\n"); + return 1; + } + memset(us->extra, 0, sizeof(struct usbat_info)); + info = (struct usbat_info *) (us->extra); - if (usbat_write_user_io(us, - USBAT_UIO_OE1 | USBAT_UIO_OE0, - USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD) + // Enable peripheral control signals + rc = usbat_write_user_io(us, + USBAT_UIO_OE1 | USBAT_UIO_OE0, + USBAT_UIO_EPAD | USBAT_UIO_1); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("INIT 1\n"); msleep(2000); - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; US_DEBUGP("INIT 2\n"); - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 3\n"); - - // Reset peripheral, enable periph control signals - // (bring reset signal up) - - if (usbat_write_user_io(us, - USBAT_UIO_DRVRST | USBAT_UIO_OE1 | USBAT_UIO_OE0, - USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD) + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 4\n"); - - // Enable periph control signals - // (bring reset signal down) + US_DEBUGP("INIT 3\n"); - if (usbat_write_user_io(us, - USBAT_UIO_OE1 | USBAT_UIO_OE0, - USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD) + // At this point, we need to detect which device we are using + if (usbat_set_transport(us, info)) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 5\n"); + US_DEBUGP("INIT 4\n"); - msleep(250); + if (usbat_get_device_type(us) == USBAT_DEV_HP8200) { + msleep(250); - // Write 0x80 to ISA port 0x3F + // Write 0x80 to ISA port 0x3F + rc = usbat_write(us, USBAT_ISA, 0x3F, 0x80); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; - if (usbat_write(us, USBAT_ISA, 0x3F, 0x80) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + US_DEBUGP("INIT 5\n"); - US_DEBUGP("INIT 6\n"); + // Read ISA port 0x27 + rc = usbat_read(us, USBAT_ISA, 0x27, status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; - // Read ISA port 0x27 + US_DEBUGP("INIT 6\n"); - if (usbat_read(us, USBAT_ISA, 0x27, status) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 7\n"); + US_DEBUGP("INIT 7\n"); + } - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + rc = usbat_select_and_test_registers(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; US_DEBUGP("INIT 8\n"); - if ( (result = hp_8200e_select_and_test_registers(us)) != - USB_STOR_TRANSPORT_GOOD) - return result; + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("INIT 9\n"); - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + // Enable peripheral control signals and card detect + rc = usbat_device_enable_cdt(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; US_DEBUGP("INIT 10\n"); - // Enable periph control signals and card detect - - if (usbat_write_user_io(us, - USBAT_UIO_ACKD |USBAT_UIO_OE1 | USBAT_UIO_OE0, - USBAT_UIO_EPAD | USBAT_UIO_1) != USB_STOR_XFER_GOOD) + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("INIT 11\n"); - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("INIT 12\n"); - msleep(1400); - if (usbat_read_user_io(us, status) != - USB_STOR_XFER_GOOD) + rc = usbat_read_user_io(us, status); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 13\n"); + US_DEBUGP("INIT 12\n"); - if ( (result = hp_8200e_select_and_test_registers(us)) != - USB_STOR_TRANSPORT_GOOD) - return result; + rc = usbat_select_and_test_registers(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; - US_DEBUGP("INIT 14\n"); + US_DEBUGP("INIT 13\n"); - if (usbat_set_shuttle_features(us, - 0x83, 0x00, 0x88, 0x08, 0x15, 0x14) != - USB_STOR_XFER_GOOD) + if (usbat_get_device_type(us) == USBAT_DEV_FLASH) { + subcountH = 0x02; + subcountL = 0x00; + } + rc = usbat_set_shuttle_features(us, (USBAT_FEAT_ETEN | USBAT_FEAT_ET2 | USBAT_FEAT_ET1), + 0x00, 0x88, 0x08, subcountH, subcountL); + if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 15\n"); + US_DEBUGP("INIT 14\n"); return USB_STOR_TRANSPORT_GOOD; } @@ -807,7 +1417,7 @@ /* * Transport for the HP 8200e */ -int hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) +int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us) { int result; unsigned char *status = us->iobuf; @@ -824,13 +1434,13 @@ commands... just ATA Packet Commands. */ - registers[0] = 0x11; - registers[1] = 0x12; - registers[2] = 0x13; - registers[3] = 0x14; - registers[4] = 0x15; - registers[5] = 0x16; - registers[6] = 0x17; + registers[0] = USBAT_ATA_FEATURES; + registers[1] = USBAT_ATA_SECCNT; + registers[2] = USBAT_ATA_SECNUM; + registers[3] = USBAT_ATA_LBA_ME; + registers[4] = USBAT_ATA_LBA_HI; + registers[5] = USBAT_ATA_DEVICE; + registers[6] = USBAT_ATA_CMD; data[0] = 0x00; data[1] = 0x00; data[2] = 0x00; @@ -844,7 +1454,7 @@ data[i] = (i-7 >= srb->cmd_len) ? 0 : srb->cmnd[i-7]; } - result = usbat_read(us, USBAT_ATA, 0x17, status); + result = usbat_get_status(us, status); US_DEBUGP("Status = %02X\n", *status); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -853,9 +1463,10 @@ if (srb->sc_data_direction == DMA_TO_DEVICE) { - result = usbat_rw_block_test(us, USBAT_ATA, + result = usbat_hp8200e_rw_block_test(us, USBAT_ATA, registers, data, 19, - 0x10, 0x17, 0xFD, 0x30, + USBAT_ATA_DATA, USBAT_ATA_STATUS, 0xFD, + (USBAT_QUAL_FCQ | USBAT_QUAL_ALQ), DMA_TO_DEVICE, srb->request_buffer, len, srb->use_sg, 10); @@ -870,7 +1481,7 @@ } else if (srb->cmnd[0] == READ_10 || srb->cmnd[0] == GPCMD_READ_CD) { - return usbat_handle_read10(us, registers, data, srb); + return usbat_hp8200e_handle_read10(us, registers, data, srb); } @@ -881,7 +1492,6 @@ } if ( (result = usbat_multiple_write(us, - USBAT_ATA, registers, data, 7)) != USB_STOR_TRANSPORT_GOOD) { return result; } @@ -895,7 +1505,7 @@ // AT SPEED 4 IS UNRELIABLE!!! if ( (result = usbat_write_block(us, - USBAT_ATA, 0x10, srb->cmnd, 12, 0, + USBAT_ATA, srb->cmnd, 12, srb->cmnd[0]==GPCMD_BLANK ? 75 : 10)) != USB_STOR_TRANSPORT_GOOD) { return result; @@ -908,14 +1518,14 @@ // How many bytes to read in? Check cylL register - if (usbat_read(us, USBAT_ATA, 0x14, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, status) != USB_STOR_XFER_GOOD) { return USB_STOR_TRANSPORT_ERROR; } if (len > 0xFF) { // need to read cylH also len = *status; - if (usbat_read(us, USBAT_ATA, 0x15, status) != + if (usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_HI, status) != USB_STOR_XFER_GOOD) { return USB_STOR_TRANSPORT_ERROR; } @@ -925,8 +1535,7 @@ len = *status; - result = usbat_read_block(us, USBAT_ATA, 0x10, - srb->request_buffer, len, srb->use_sg); + result = usbat_read_block(us, srb->request_buffer, len); /* Debug-print the first 32 bytes of the transfer */ @@ -948,4 +1557,153 @@ return result; } +/* + * Transport for USBAT02-based CompactFlash and similar storage devices + */ +int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us) +{ + int rc; + struct usbat_info *info = (struct usbat_info *) (us->extra); + unsigned long block, blocks; + unsigned char *ptr = us->iobuf; + static unsigned char inquiry_response[36] = { + 0x00, 0x80, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x00 + }; + + if (srb->cmnd[0] == INQUIRY) { + US_DEBUGP("usbat_flash_transport: INQUIRY. Returning bogus response.\n"); + memcpy(ptr, inquiry_response, sizeof(inquiry_response)); + fill_inquiry_response(us, ptr, 36); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_CAPACITY) { + rc = usbat_flash_check_media(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + rc = usbat_flash_get_sector_count(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + info->ssize = 0x200; // hard coded 512 byte sectors as per ATA spec + US_DEBUGP("usbat_flash_transport: READ_CAPACITY: %ld sectors, %ld bytes per sector\n", + info->sectors, info->ssize); + + // build the reply + // note: must return the sector number of the last sector, + // *not* the total number of sectors + ((__be32 *) ptr)[0] = cpu_to_be32(info->sectors - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(info->ssize); + usb_stor_set_xfer_buf(ptr, 8, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == MODE_SELECT_10) { + US_DEBUGP("usbat_flash_transport: Gah! MODE_SELECT_10.\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (srb->cmnd[0] == READ_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + US_DEBUGP("usbat_flash_transport: READ_10: read block 0x%04lx count %ld\n", block, blocks); + return usbat_flash_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == READ_12) { + // I don't think we'll ever see a READ_12 but support it anyway... + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + US_DEBUGP("usbat_flash_transport: READ_12: read block 0x%04lx count %ld\n", block, blocks); + return usbat_flash_read_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_10) { + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[7]) << 8) | ((u32)(srb->cmnd[8])); + + US_DEBUGP("usbat_flash_transport: WRITE_10: write block 0x%04lx count %ld\n", block, blocks); + return usbat_flash_write_data(us, info, block, blocks); + } + + if (srb->cmnd[0] == WRITE_12) { + // I don't think we'll ever see a WRITE_12 but support it anyway... + block = ((u32)(srb->cmnd[2]) << 24) | ((u32)(srb->cmnd[3]) << 16) | + ((u32)(srb->cmnd[4]) << 8) | ((u32)(srb->cmnd[5])); + + blocks = ((u32)(srb->cmnd[6]) << 24) | ((u32)(srb->cmnd[7]) << 16) | + ((u32)(srb->cmnd[8]) << 8) | ((u32)(srb->cmnd[9])); + + US_DEBUGP("usbat_flash_transport: WRITE_12: write block 0x%04lx count %ld\n", block, blocks); + return usbat_flash_write_data(us, info, block, blocks); + } + + + if (srb->cmnd[0] == TEST_UNIT_READY) { + US_DEBUGP("usbat_flash_transport: TEST_UNIT_READY.\n"); + + rc = usbat_flash_check_media(us, info); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + return usbat_check_status(us); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + US_DEBUGP("usbat_flash_transport: REQUEST_SENSE.\n"); + + memset(ptr, 0, 18); + ptr[0] = 0xF0; + ptr[2] = info->sense_key; + ptr[7] = 11; + ptr[12] = info->sense_asc; + ptr[13] = info->sense_ascq; + usb_stor_set_xfer_buf(ptr, 18, srb); + + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) { + // sure. whatever. not like we can stop the user from popping + // the media out of the device (no locking doors, etc) + return USB_STOR_TRANSPORT_GOOD; + } + + US_DEBUGP("usbat_flash_transport: Gah! Unknown command: %d (0x%x)\n", + srb->cmnd[0], srb->cmnd[0]); + info->sense_key = 0x05; + info->sense_asc = 0x20; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; +} + +/* + * Default transport function. Attempts to detect which transport function + * should be called, makes it the new default, and calls it. + * + * This function should never be called. Our usbat_init() function detects the + * device type and changes the us->transport ptr to the transport function + * relevant to the device. + * However, we'll support this impossible(?) case anyway. + */ +int usbat_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + struct usbat_info *info = (struct usbat_info*) (us->extra); + + if (usbat_set_transport(us, info)) + return USB_STOR_TRANSPORT_ERROR; + + return us->transport(srb, us); +} diff -Nru a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h --- a/drivers/usb/storage/shuttle_usbat.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/shuttle_usbat.h 2005-02-04 22:22:37 -08:00 @@ -5,8 +5,9 @@ * * Current development and maintenance by: * (c) 2000 Robert Baruch (autophile@dol.net) + * (c) 2004, 2005 Daniel Drake * - * See scm.c for more explanation + * See shuttle_usbat.c for more explanation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -26,13 +27,59 @@ #ifndef _USB_SHUTTLE_USBAT_H #define _USB_SHUTTLE_USBAT_H +/* Supported device types */ +#define USBAT_DEV_HP8200 0x01 +#define USBAT_DEV_FLASH 0x02 + #define USBAT_EPP_PORT 0x10 #define USBAT_EPP_REGISTER 0x30 #define USBAT_ATA 0x40 #define USBAT_ISA 0x50 -/* SCM User I/O Data registers */ +/* Commands (need to be logically OR'd with an access type */ +#define USBAT_CMD_READ_REG 0x00 +#define USBAT_CMD_WRITE_REG 0x01 +#define USBAT_CMD_READ_BLOCK 0x02 +#define USBAT_CMD_WRITE_BLOCK 0x03 +#define USBAT_CMD_COND_READ_BLOCK 0x04 +#define USBAT_CMD_COND_WRITE_BLOCK 0x05 +#define USBAT_CMD_WRITE_REGS 0x07 + +/* Commands (these don't need an access type) */ +#define USBAT_CMD_EXEC_CMD 0x80 +#define USBAT_CMD_SET_FEAT 0x81 +#define USBAT_CMD_UIO 0x82 + +/* Methods of accessing UIO register */ +#define USBAT_UIO_READ 1 +#define USBAT_UIO_WRITE 0 + +/* Qualifier bits */ +#define USBAT_QUAL_FCQ 0x20 // full compare +#define USBAT_QUAL_ALQ 0x10 // auto load subcount + +/* USBAT Flash Media status types */ +#define USBAT_FLASH_MEDIA_NONE 0 +#define USBAT_FLASH_MEDIA_CF 1 + +/* USBAT Flash Media change types */ +#define USBAT_FLASH_MEDIA_SAME 0 +#define USBAT_FLASH_MEDIA_CHANGED 1 + +/* USBAT ATA registers */ +#define USBAT_ATA_DATA 0x10 // read/write data (R/W) +#define USBAT_ATA_FEATURES 0x11 // set features (W) +#define USBAT_ATA_ERROR 0x11 // error (R) +#define USBAT_ATA_SECCNT 0x12 // sector count (R/W) +#define USBAT_ATA_SECNUM 0x13 // sector number (R/W) +#define USBAT_ATA_LBA_ME 0x14 // cylinder low (R/W) +#define USBAT_ATA_LBA_HI 0x15 // cylinder high (R/W) +#define USBAT_ATA_DEVICE 0x16 // head/device selection (R/W) +#define USBAT_ATA_STATUS 0x17 // device status (R) +#define USBAT_ATA_CMD 0x17 // device command (W) +#define USBAT_ATA_ALTSTATUS 0x0E // status (no clear IRQ) (R) +/* USBAT User I/O Data registers */ #define USBAT_UIO_EPAD 0x80 // Enable Peripheral Control Signals #define USBAT_UIO_CDT 0x40 // Card Detect (Read Only) // CDT = ACKD & !UI1 & !UI0 @@ -43,8 +90,7 @@ #define USBAT_UIO_UI0 0x02 // Input 0 #define USBAT_UIO_INTR_ACK 0x01 // Interrupt (ATA & ISA)/Acknowledge (EPP) -/* SCM User I/O Enable registers */ - +/* USBAT User I/O Enable registers */ #define USBAT_UIO_DRVRST 0x80 // Reset Peripheral #define USBAT_UIO_ACKD 0x40 // Enable Card Detect #define USBAT_UIO_OE1 0x20 // I/O 1 set=output/clr=input @@ -52,8 +98,30 @@ #define USBAT_UIO_OE0 0x10 // I/O 0 set=output/clr=input #define USBAT_UIO_ADPRST 0x01 // Reset SCM chip -/* HP 8200e stuff */ -extern int hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us); -extern int init_8200e(struct us_data *us); +/* USBAT Features */ +#define USBAT_FEAT_ETEN 0x80 // External trigger enable +#define USBAT_FEAT_U1 0x08 +#define USBAT_FEAT_U0 0x04 +#define USBAT_FEAT_ET1 0x02 +#define USBAT_FEAT_ET2 0x01 + +/* Transport functions */ +int usbat_hp8200e_transport(struct scsi_cmnd *srb, struct us_data *us); +int usbat_flash_transport(struct scsi_cmnd * srb, struct us_data *us); + +extern int usbat_transport(struct scsi_cmnd *srb, struct us_data *us); +extern int init_usbat(struct us_data *us); + +struct usbat_info { + int devicetype; + + /* Used for Flash readers only */ + unsigned long sectors; // total sector count + unsigned long ssize; // sector size in bytes + + unsigned char sense_key; + unsigned long sense_asc; // additional sense code + unsigned long sense_ascq; // additional sense code qualifier +}; #endif diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c --- a/drivers/usb/storage/transport.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/transport.c 2005-02-04 22:22:37 -08:00 @@ -1055,19 +1055,30 @@ US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", le32_to_cpu(bcs->Signature), bcs->Tag, residue, bcs->Status); - if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) && - bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) || - bcs->Tag != srb->serial_number || - bcs->Status > US_BULK_STAT_PHASE) { + if (bcs->Tag != srb->serial_number || bcs->Status > US_BULK_STAT_PHASE) { US_DEBUGP("Bulk logical error\n"); return USB_STOR_TRANSPORT_ERROR; } + /* Some broken devices report odd signatures, so we do not check them + * for validity against the spec. We store the first one we see, + * and check subsequent transfers for validity against this signature. + */ + if (!us->bcs_signature) { + us->bcs_signature = bcs->Signature; + US_DEBUGP("Learnt BCS signature 0x%08X\n", + le32_to_cpu(us->bcs_signature)); + } else if (bcs->Signature != us->bcs_signature) { + US_DEBUGP("Signature mismatch: device sent %08X, expecting %08X", + le32_to_cpu(bcs->Signature), + le32_to_cpu(us->bcs_signature)); + return USB_STOR_TRANSPORT_ERROR; + } + /* try to compute the actual residue, based on how much data * was really transferred and what the device tells us */ if (residue) { - if (!(us->flags & US_FL_IGNORE_RESIDUE) || - srb->sc_data_direction == DMA_TO_DEVICE) { + if (!(us->flags & US_FL_IGNORE_RESIDUE)) { residue = min(residue, transfer_length); srb->resid = max(srb->resid, (int) residue); } diff -Nru a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h --- a/drivers/usb/storage/transport.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/transport.h 2005-02-04 22:22:37 -08:00 @@ -52,7 +52,7 @@ #define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */ #define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */ #define US_PR_BULK 0x50 /* bulk only */ -#ifdef CONFIG_USB_STORAGE_HP8200e +#ifdef CONFIG_USB_STORAGE_USBAT #define US_PR_SCM_ATAPI 0x80 /* SCM-ATAPI bridge */ #endif #ifdef CONFIG_USB_STORAGE_SDDR09 @@ -107,9 +107,6 @@ }; #define US_BULK_CS_WRAP_LEN 13 -#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ -/* This is for Olympus Camedia digital cameras */ -#define US_BULK_CS_OLYMPUS_SIGN 0x55425355 /* spells out 'USBU' */ #define US_BULK_STAT_OK 0 #define US_BULK_STAT_FAIL 1 #define US_BULK_STAT_PHASE 2 diff -Nru a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h --- a/drivers/usb/storage/unusual_devs.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/unusual_devs.h 2005-02-04 22:22:37 -08:00 @@ -59,16 +59,16 @@ "CD-Writer+", US_SC_8070, US_PR_CB, NULL, 0), -#ifdef CONFIG_USB_STORAGE_HP8200e +#ifdef CONFIG_USB_STORAGE_USBAT UNUSUAL_DEV( 0x03f0, 0x0207, 0x0001, 0x0001, "HP", "CD-Writer+ 8200e", - US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), + US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0), UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, "HP", "CD-Writer+ CD-4e", - US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), + US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0), #endif /* Deduced by Jonathan Woithe @@ -171,15 +171,12 @@ "CD-R/RW Drive", US_SC_8070, US_PR_CB, NULL, 0), -/* Reported by Adriaan Penning - * Note that these cameras report "Medium not present" after - * ALLOW_MEDIUM_REMOVAL, so they also need to be marked - * NOT_LOCKABLE in the SCSI blacklist (and the vendor is MATSHITA). */ +/* Reported by Adriaan Penning */ UNUSUAL_DEV( 0x04da, 0x2372, 0x0000, 0x9999, "Panasonic", "DMC-LCx Camera", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), + US_FL_FIX_CAPACITY | US_FL_NOT_LOCKABLE ), /* Most of the following entries were developed with the help of * Shuttle/SCM directly. @@ -268,6 +265,14 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_BULK32), +#ifdef CONFIG_USB_STORAGE_USBAT +UNUSUAL_DEV( 0x04e6, 0x1010, 0x0000, 0x9999, + "SCM", + "SCM USBAT-02", + US_SC_SCSI, US_PR_SCM_ATAPI, init_usbat, + US_FL_SINGLE_LUN), +#endif + /* Reported by Bob Sass -- only rev 1.33 tested */ UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133, "Belkin", @@ -324,12 +329,11 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -/* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0106, 0x0450, "Sony", "DSC-S30/S70/S75/505V/F505/F707/F717/P8", US_SC_SCSI, US_PR_DEVICE, NULL, - US_FL_SINGLE_LUN ), + US_FL_SINGLE_LUN | US_FL_NOT_LOCKABLE ), /* This entry is needed because the device reports Sub=ff */ UNUSUAL_DEV( 0x054c, 0x0010, 0x0500, 0x0500, @@ -582,12 +586,18 @@ US_SC_SCSI, US_PR_CB, NULL, US_FL_SINGLE_LUN ), -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) UNUSUAL_DEV( 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk", "ImageMate SDDR-31", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_SER ), + +#ifdef CONFIG_USB_STORAGE_USBAT +UNUSUAL_DEV( 0x0781, 0x0005, 0x0005, 0x0005, + "Sandisk", + "ImageMate SDDR-05b", + US_SC_SCSI, US_PR_SCM_ATAPI, init_usbat, + US_FL_SINGLE_LUN), #endif UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100, @@ -863,6 +873,13 @@ UNUSUAL_DEV( 0x0dda, 0x0001, 0x0012, 0x0012, "WINWARD", "Music Disk", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + +/* Reported by Ian McConnell */ +UNUSUAL_DEV( 0x0dda, 0x0301, 0x0012, 0x0012, + "PNP_MP3", + "PNP_MP3 PLAYER", US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), diff -Nru a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c --- a/drivers/usb/storage/usb.c 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/usb.c 2005-02-04 22:22:37 -08:00 @@ -63,7 +63,7 @@ #include "debug.h" #include "initializers.h" -#ifdef CONFIG_USB_STORAGE_HP8200e +#ifdef CONFIG_USB_STORAGE_USBAT #include "shuttle_usbat.h" #endif #ifdef CONFIG_USB_STORAGE_SDDR09 @@ -144,9 +144,7 @@ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) }, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, -#endif /* Terminating entry */ { } @@ -220,10 +218,8 @@ .useTransport = US_PR_BULK}, { .useProtocol = US_SC_8070, .useTransport = US_PR_BULK}, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) { .useProtocol = US_SC_SCSI, .useTransport = US_PR_BULK}, -#endif /* Terminating entry */ { NULL } @@ -572,10 +568,10 @@ us->transport_reset = usb_stor_Bulk_reset; break; -#ifdef CONFIG_USB_STORAGE_HP8200e +#ifdef CONFIG_USB_STORAGE_USBAT case US_PR_SCM_ATAPI: us->transport_name = "SCM/ATAPI"; - us->transport = hp8200e_transport; + us->transport = usbat_transport; us->transport_reset = usb_stor_CB_reset; us->max_lun = 1; break; diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h 2005-02-04 22:22:37 -08:00 +++ b/drivers/usb/storage/usb.h 2005-02-04 22:22:37 -08:00 @@ -75,6 +75,7 @@ #define US_FL_FIX_CAPACITY 0x00000080 /* READ CAPACITY response too big */ #define US_FL_IGNORE_RESIDUE 0x00000100 /* reported residue is wrong */ #define US_FL_BULK32 0x00000200 /* Uses 32-byte CBW length */ +#define US_FL_NOT_LOCKABLE 0x00000400 /* PREVENT/ALLOW not supported */ /* Dynamic flag definitions: used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ @@ -126,6 +127,7 @@ char serial[USB_STOR_STRING_LEN]; char *transport_name; char *protocol_name; + __le32 bcs_signature; u8 subclass; u8 protocol; u8 max_lun; diff -Nru a/include/linux/usb.h b/include/linux/usb.h --- a/include/linux/usb.h 2005-02-04 22:22:37 -08:00 +++ b/include/linux/usb.h 2005-02-04 22:22:37 -08:00 @@ -285,6 +285,10 @@ struct class_device class_dev; /* class device for this bus */ void (*release)(struct usb_bus *bus); /* function to destroy this bus's memory */ +#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) + struct mon_bus *mon_bus; /* non-null when associated */ + int monitored; /* non-zero when monitored */ +#endif }; #define to_usb_bus(d) container_of(d, struct usb_bus, class_dev) diff -Nru a/include/linux/usb_cdc.h b/include/linux/usb_cdc.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/linux/usb_cdc.h 2005-02-04 22:22:37 -08:00 @@ -0,0 +1,162 @@ +/* + * USB Communications Device Class (CDC) definitions + * + * CDC says how to talk to lots of different types of network adapters, + * notably ethernet adapters and various modems. It's used mostly with + * firmware based USB peripherals. + */ + +#define USB_CDC_SUBCLASS_ACM 2 +#define USB_CDC_SUBCLASS_ETHERNET 6 + +#define USB_CDC_PROTO_NONE 0 + +#define USB_CDC_ACM_PROTO_AT_V25TER 1 +#define USB_CDC_ACM_PROTO_AT_PCCA101 2 +#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 +#define USB_CDC_ACM_PROTO_AT_GSM 4 +#define USB_CDC_ACM_PROTO_AT_3G 5 +#define USB_CDC_ACM_PROTO_AT_CDMA 6 +#define USB_CDC_ACM_PROTO_VENDOR 0xff + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific descriptors ... there are a couple dozen of them + */ + +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_COUNTRY_TYPE 0x07 +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ + +/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ +struct usb_cdc_header_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdCDC; +} __attribute__ ((packed)); + +/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ +struct usb_cdc_call_mgmt_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 +#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 + + __u8 bDataInterface; +} __attribute__ ((packed)); + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ +struct usb_cdc_acm_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +} __attribute__ ((packed)); + +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ +struct usb_cdc_union_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bMasterInterface0; + __u8 bSlaveInterface0; + /* ... and there could be other slave interfaces */ +} __attribute__ ((packed)); + +/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ +struct usb_cdc_ether_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 iMACAddress; + __le32 bmEthernetStatistics; + __le16 wMaxSegmentSize; + __le16 wNumberMCFilters; + __u8 bNumberPowerFilters; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Control Requests (6.2) + * + * section 3.6.2.1 table 4 has the ACM profile, for modems. + * section 3.8.2 table 10 has the ethernet profile. + * + * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, + * heavily dependent on the encapsulated (proprietary) command mechanism. + */ + +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 + +/* Line Coding Structure from CDC spec 6.2.13 */ +struct usb_cdc_line_coding { + __le32 dwDTERate; + __u8 bCharFormat; +#define USB_CDC_1_STOP_BITS 0 +#define USB_CDC_1_5_STOP_BITS 1 +#define USB_CDC_2_STOP_BITS 2 + + __u8 bParityType; +#define USB_CDC_NO_PARITY 0 +#define USB_CDC_ODD_PARITY 1 +#define USB_CDC_EVEN_PARITY 2 +#define USB_CDC_MARK_PARITY 3 +#define USB_CDC_SPACE_PARITY 4 + + __u8 bDataBits; +} __attribute__ ((packed)); + +/* table 62; bits in multicast filter */ +#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) +#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ +#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) +#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) +#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ + + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Notifications (6.3) sent by interrupt transfers + * + * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * RNDIS also defines its own bit-incompatible notifications + */ + +#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 +#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 +#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a + +struct usb_cdc_notification { + __u8 bmRequestType; + __u8 bNotificationType; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); +