diff options
Diffstat (limited to 'drivers')
189 files changed, 6352 insertions, 2015 deletions
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 7b1cd93892b..c4b9d2adfc0 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -358,7 +358,8 @@ config BLK_DEV_UB This driver supports certain USB attached storage devices such as flash keys. - Warning: Enabling this cripples the usb-storage driver. + If you enable this driver, it is recommended to avoid conflicts + with usb-storage by enabling USB_LIBUSUAL. If unsure, say N. diff --git a/drivers/block/ub.c b/drivers/block/ub.c index bfb23d543ff..10740a06508 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -9,7 +9,6 @@ * * TODO (sorted by decreasing priority) * -- Kill first_open (Al Viro fixed the block layer now) - * -- Do resets with usb_device_reset (needs a thread context, use khubd) * -- set readonly flag for CDs, set removable flag for CF readers * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch) * -- special case some senses, e.g. 3a/0 -> no media present, reduce retries @@ -29,6 +28,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> +#include <linux/usb_usual.h> #include <linux/blkdev.h> #include <linux/devfs_fs_kernel.h> #include <linux/timer.h> @@ -107,16 +107,6 @@ */ /* - * Definitions which have to be scattered once we understand the layout better. - */ - -/* Transport (despite PR in the name) */ -#define US_PR_BULK 0x50 /* bulk only */ - -/* Protocol */ -#define US_SC_SCSI 0x06 /* Transparent */ - -/* * This many LUNs per USB device. * Every one of them takes a host, see UB_MAX_HOSTS. */ @@ -125,7 +115,7 @@ /* */ -#define UB_MINORS_PER_MAJOR 8 +#define UB_PARTS_PER_LUN 8 #define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */ @@ -245,6 +235,13 @@ struct ub_scsi_cmd { void *back; }; +struct ub_request { + struct request *rq; + unsigned int current_try; + unsigned int nsg; /* sgv[nsg] */ + struct scatterlist sgv[UB_MAX_REQ_SG]; +}; + /* */ struct ub_capacity { @@ -340,6 +337,8 @@ struct ub_lun { int readonly; int first_open; /* Kludge. See ub_bd_open. */ + struct ub_request urq; + /* Use Ingo's mempool if or when we have more than one command. */ /* * Currently we never need more than one command for the whole device. @@ -360,6 +359,7 @@ struct ub_dev { atomic_t poison; /* The USB device is disconnected */ int openc; /* protected by ub_lock! */ /* kref is too implicit for our taste */ + int reset; /* Reset is running */ unsigned int tagcnt; char name[12]; struct usb_device *dev; @@ -387,6 +387,9 @@ struct ub_dev { struct bulk_cs_wrap work_bcs; struct usb_ctrlrequest work_cr; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + int sg_stat[6]; struct ub_scsi_trace tr; }; @@ -395,12 +398,14 @@ struct ub_dev { */ static void ub_cleanup(struct ub_dev *sc); static int ub_request_fn_1(struct ub_lun *lun, struct request *rq); -static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq); -static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq); +static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq); +static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq); 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_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun, + struct ub_request *urq, struct ub_scsi_cmd *cmd); static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static void ub_urb_complete(struct urb *urb, struct pt_regs *pt); static void ub_scsi_action(unsigned long _dev); @@ -415,6 +420,8 @@ static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd); static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int stalled_pipe); static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd); +static void ub_reset_enter(struct ub_dev *sc); +static void ub_reset_task(void *arg); static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun); static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun, struct ub_capacity *ret); @@ -422,13 +429,18 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum); /* */ +#ifdef CONFIG_USB_LIBUSUAL + +#define ub_usb_ids storage_usb_ids +#else + static struct usb_device_id ub_usb_ids[] = { - // { USB_DEVICE_VER(0x0781, 0x0002, 0x0009, 0x0009) }, /* SDDR-31 */ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, { } }; MODULE_DEVICE_TABLE(usb, ub_usb_ids); +#endif /* CONFIG_USB_LIBUSUAL */ /* * Find me a way to identify "next free minor" for add_disk(), @@ -522,6 +534,9 @@ static ssize_t ub_diag_show(struct device *dev, struct device_attribute *attr, spin_lock_irqsave(&sc->lock, flags); cnt += sprintf(page + cnt, + "poison %d reset %d\n", + atomic_read(&sc->poison), sc->reset); + cnt += sprintf(page + cnt, "qlen %d qmax %d\n", sc->cmd_queue.qlen, sc->cmd_queue.qmax); cnt += sprintf(page + cnt, @@ -770,7 +785,8 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq) { struct ub_dev *sc = lun->udev; struct ub_scsi_cmd *cmd; - int rc; + struct ub_request *urq; + int n_elem; if (atomic_read(&sc->poison) || lun->changed) { blkdev_dequeue_request(rq); @@ -778,65 +794,70 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq) return 0; } + if (lun->urq.rq != NULL) + return -1; if ((cmd = ub_get_cmd(lun)) == NULL) return -1; memset(cmd, 0, sizeof(struct ub_scsi_cmd)); blkdev_dequeue_request(rq); + + urq = &lun->urq; + memset(urq, 0, sizeof(struct ub_request)); + urq->rq = rq; + + /* + * get scatterlist from block layer + */ + n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]); + if (n_elem < 0) { + printk(KERN_INFO "%s: failed request map (%d)\n", + lun->name, n_elem); /* P3 */ + goto drop; + } + if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ + printk(KERN_WARNING "%s: request with %d segments\n", + lun->name, n_elem); + goto drop; + } + urq->nsg = n_elem; + sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + if (blk_pc_request(rq)) { - rc = ub_cmd_build_packet(sc, lun, cmd, rq); + ub_cmd_build_packet(sc, lun, cmd, urq); } else { - rc = ub_cmd_build_block(sc, lun, cmd, rq); - } - if (rc != 0) { - ub_put_cmd(lun, cmd); - ub_end_rq(rq, 0); - return 0; + ub_cmd_build_block(sc, lun, cmd, urq); } cmd->state = UB_CMDST_INIT; cmd->lun = lun; cmd->done = ub_rw_cmd_done; - cmd->back = rq; + cmd->back = urq; cmd->tag = sc->tagcnt++; - if (ub_submit_scsi(sc, cmd) != 0) { - ub_put_cmd(lun, cmd); - ub_end_rq(rq, 0); - return 0; - } + if (ub_submit_scsi(sc, cmd) != 0) + goto drop; return 0; + +drop: + ub_put_cmd(lun, cmd); + ub_end_rq(rq, 0); + return 0; } -static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq) +static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq) { - int ub_dir; - int n_elem; + struct request *rq = urq->rq; unsigned int block, nblks; if (rq_data_dir(rq) == WRITE) - ub_dir = UB_DIR_WRITE; + cmd->dir = UB_DIR_WRITE; else - ub_dir = UB_DIR_READ; - cmd->dir = ub_dir; + cmd->dir = UB_DIR_READ; - /* - * get scatterlist from block layer - */ - n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]); - if (n_elem <= 0) { - printk(KERN_INFO "%s: failed request map (%d)\n", - sc->name, n_elem); /* P3 */ - return -1; /* request with no s/g entries? */ - } - if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ - printk(KERN_WARNING "%s: request with %d segments\n", - sc->name, n_elem); - return -1; - } - cmd->nsg = n_elem; - sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + cmd->nsg = urq->nsg; + memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg); /* * build the command @@ -847,7 +868,7 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, block = rq->sector >> lun->capacity.bshift; nblks = rq->nr_sectors >> lun->capacity.bshift; - cmd->cdb[0] = (ub_dir == UB_DIR_READ)? READ_10: WRITE_10; + cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10; /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */ cmd->cdb[2] = block >> 24; cmd->cdb[3] = block >> 16; @@ -858,14 +879,12 @@ static int ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun, cmd->cdb_len = 10; cmd->len = rq->nr_sectors * 512; - - return 0; } -static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, - struct ub_scsi_cmd *cmd, struct request *rq) +static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, + struct ub_scsi_cmd *cmd, struct ub_request *urq) { - int n_elem; + struct request *rq = urq->rq; if (rq->data_len == 0) { cmd->dir = UB_DIR_NONE; @@ -874,40 +893,26 @@ static int ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun, cmd->dir = UB_DIR_WRITE; else cmd->dir = UB_DIR_READ; - } - /* - * get scatterlist from block layer - */ - n_elem = blk_rq_map_sg(lun->disk->queue, rq, &cmd->sgv[0]); - if (n_elem < 0) { - printk(KERN_INFO "%s: failed request map (%d)\n", - sc->name, n_elem); /* P3 */ - return -1; - } - if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */ - printk(KERN_WARNING "%s: request with %d segments\n", - sc->name, n_elem); - return -1; - } - cmd->nsg = n_elem; - sc->sg_stat[n_elem < 5 ? n_elem : 5]++; + cmd->nsg = urq->nsg; + memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg); memcpy(&cmd->cdb, rq->cmd, rq->cmd_len); cmd->cdb_len = rq->cmd_len; cmd->len = rq->data_len; - - return 0; } static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) { - struct request *rq = cmd->back; struct ub_lun *lun = cmd->lun; + struct ub_request *urq = cmd->back; + struct request *rq; int uptodate; + rq = urq->rq; + if (cmd->error == 0) { uptodate = 1; @@ -928,9 +933,16 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd) rq->errors = SAM_STAT_CHECK_CONDITION; else rq->errors = DID_ERROR << 16; + } else { + if (cmd->error == -EIO) { + if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0) + return; + } } } + urq->rq = NULL; + ub_put_cmd(lun, cmd); ub_end_rq(rq, uptodate); blk_start_queue(lun->disk->queue); @@ -938,13 +950,45 @@ 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) { - int rc; - - rc = end_that_request_first(rq, uptodate, rq->hard_nr_sectors); - // assert(rc == 0); + end_that_request_first(rq, uptodate, rq->hard_nr_sectors); end_that_request_last(rq); } +static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun, + struct ub_request *urq, struct ub_scsi_cmd *cmd) +{ + + if (atomic_read(&sc->poison)) + return -ENXIO; + + ub_reset_enter(sc); + + if (urq->current_try >= 3) + return -EIO; + urq->current_try++; + /* P3 */ printk("%s: dir %c len/act %d/%d " + "[sense %x %02x %02x] retry %d\n", + sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len, + cmd->key, cmd->asc, cmd->ascq, urq->current_try); + + memset(cmd, 0, sizeof(struct ub_scsi_cmd)); + ub_cmd_build_block(sc, lun, cmd, urq); + + cmd->state = UB_CMDST_INIT; + cmd->lun = lun; + cmd->done = ub_rw_cmd_done; + cmd->back = urq; + + cmd->tag = sc->tagcnt++; + +#if 0 /* Wasteful */ + return ub_submit_scsi(sc, cmd); +#else + ub_cmdq_add(sc, cmd); + return 0; +#endif +} + /* * Submit a regular SCSI operation (not an auto-sense). * @@ -1075,7 +1119,7 @@ static void ub_scsi_dispatch(struct ub_dev *sc) struct ub_scsi_cmd *cmd; int rc; - while ((cmd = ub_cmdq_peek(sc)) != NULL) { + while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) { if (cmd->state == UB_CMDST_DONE) { ub_cmdq_pop(sc); (*cmd->done)(sc, cmd); @@ -1098,11 +1142,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) { struct urb *urb = &sc->work_urb; struct bulk_cs_wrap *bcs; + int len; int rc; if (atomic_read(&sc->poison)) { - /* A little too simplistic, I feel... */ - goto Bad_End; + ub_state_done(sc, cmd, -ENODEV); + return; } if (cmd->state == UB_CMDST_CLEAR) { @@ -1110,7 +1155,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) /* * STALL while clearning STALL. * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); @@ -1129,11 +1173,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) } else if (cmd->state == UB_CMDST_CLR2STS) { if (urb->status == -EPIPE) { - /* - * STALL while clearning STALL. - * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. - */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); goto Bad_End; @@ -1151,11 +1190,6 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) } else if (cmd->state == UB_CMDST_CLRRS) { if (urb->status == -EPIPE) { - /* - * STALL while clearning STALL. - * The control pipe clears itself - nothing to do. - * XXX Might try to reset the device here and retry. - */ printk(KERN_NOTICE "%s: stall on control pipe\n", sc->name); goto Bad_End; @@ -1172,7 +1206,12 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_state_stat_counted(sc, cmd); } else if (cmd->state == UB_CMDST_CMD) { - if (urb->status == -EPIPE) { + switch (urb->status) { + case 0: + break; + case -EOVERFLOW: + goto Bad_End; + case -EPIPE: rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe); if (rc != 0) { printk(KERN_NOTICE "%s: " @@ -1182,17 +1221,20 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) * This is typically ENOMEM or some other such shit. * Retrying is pointless. Just do Bad End on it... */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } cmd->state = UB_CMDST_CLEAR; ub_cmdtr_state(sc, cmd); return; - } - if (urb->status != 0) { + case -ESHUTDOWN: /* unplug */ + case -EILSEQ: /* unplug timeout on uhci */ + ub_state_done(sc, cmd, -ENODEV); + return; + default: goto Bad_End; } if (urb->actual_length != US_BULK_CB_WRAP_LEN) { - /* XXX Must do reset here to unconfuse the device */ goto Bad_End; } @@ -1211,11 +1253,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); - /* - * This is typically ENOMEM or some other such shit. - * Retrying is pointless. Just do Bad End on it... - */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } cmd->state = UB_CMDST_CLR2STS; ub_cmdtr_state(sc, cmd); @@ -1224,14 +1263,50 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) if (urb->status == -EOVERFLOW) { /* * A babble? Failure, but we must transfer CSW now. - * XXX This is going to end in perpetual babble. Reset. */ cmd->error = -EOVERFLOW; /* A cheap trick... */ ub_state_stat(sc, cmd); return; } - if (urb->status != 0) - goto Bad_End; + + if (cmd->dir == UB_DIR_WRITE) { + /* + * Do not continue writes in case of a failure. + * Doing so would cause sectors to be mixed up, + * which is worse than sectors lost. + * + * We must try to read the CSW, or many devices + * get confused. + */ + len = urb->actual_length; + if (urb->status != 0 || + len != cmd->sgv[cmd->current_sg].length) { + cmd->act_len += len; + ub_cmdtr_act_len(sc, cmd); + + cmd->error = -EIO; + ub_state_stat(sc, cmd); + return; + } + + } else { + /* + * If an error occurs on read, we record it, and + * continue to fetch data in order to avoid bubble. + * + * As a small shortcut, we stop if we detect that + * a CSW mixed into data. + */ + if (urb->status != 0) + cmd->error = -EIO; + + len = urb->actual_length; + if (urb->status != 0 || + len != cmd->sgv[cmd->current_sg].length) { + if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN) + goto Bad_End; + } + } cmd->act_len += urb->actual_length; ub_cmdtr_act_len(sc, cmd); @@ -1249,11 +1324,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_NOTICE "%s: " "unable to submit clear (%d)\n", sc->name, rc); - /* - * This is typically ENOMEM or some other such shit. - * Retrying is pointless. Just do Bad End on it... - */ - goto Bad_End; + ub_state_done(sc, cmd, rc); + return; } /* @@ -1266,14 +1338,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_cmdtr_state(sc, cmd); return; } - if (urb->status == -EOVERFLOW) { - /* - * XXX We are screwed here. Retrying is pointless, - * because the pipelined data will not get in until - * we read with a big enough buffer. We must reset XXX. - */ - goto Bad_End; - } + + /* Catch everything, including -EOVERFLOW and other nasties. */ if (urb->status != 0) goto Bad_End; @@ -1319,15 +1385,15 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) return; } - rc = le32_to_cpu(bcs->Residue); - if (rc != cmd->len - cmd->act_len) { + len = le32_to_cpu(bcs->Residue); + if (len != cmd->len - cmd->act_len) { /* * It is all right to transfer less, the caller has * to check. But it's not all right if the device * counts disagree with our counts. */ /* P3 */ printk("%s: resid %d len %d act %d\n", - sc->name, rc, cmd->len, cmd->act_len); + sc->name, len, cmd->len, cmd->act_len); goto Bad_End; } @@ -1338,13 +1404,13 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) ub_state_sense(sc, cmd); return; case US_BULK_STAT_PHASE: - /* XXX We must reset the transport here */ /* P3 */ printk("%s: status PHASE\n", sc->name); goto Bad_End; default: printk(KERN_INFO "%s: unknown CSW status 0x%x\n", sc->name, bcs->Status); - goto Bad_End; + ub_state_done(sc, cmd, -EINVAL); + return; } /* Not zeroing error to preserve a babble indicator */ @@ -1364,7 +1430,8 @@ static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd) printk(KERN_WARNING "%s: " "wrong command state %d\n", sc->name, cmd->state); - goto Bad_End; + ub_state_done(sc, cmd, -EINVAL); + return; } return; @@ -1612,6 +1679,93 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) } /* + * Reset management + */ + +static void ub_reset_enter(struct ub_dev *sc) +{ + + if (sc->reset) { + /* This happens often on multi-LUN devices. */ + return; + } + sc->reset = 1; + +#if 0 /* Not needed because the disconnect waits for us. */ + unsigned long flags; + spin_lock_irqsave(&ub_lock, flags); + sc->openc++; + spin_unlock_irqrestore(&ub_lock, flags); +#endif + +#if 0 /* We let them stop themselves. */ + struct list_head *p; + struct ub_lun *lun; + list_for_each(p, &sc->luns) { + lun = list_entry(p, struct ub_lun, link); + blk_stop_queue(lun->disk->queue); + } +#endif + + schedule_work(&sc->reset_work); +} + +static void ub_reset_task(void *arg) +{ + struct ub_dev *sc = arg; + unsigned long flags; + struct list_head *p; + struct ub_lun *lun; + int lkr, rc; + + if (!sc->reset) { + printk(KERN_WARNING "%s: Running reset unrequested\n", + sc->name); + return; + } + + if (atomic_read(&sc->poison)) { + printk(KERN_NOTICE "%s: Not resetting disconnected device\n", + sc->name); /* P3 This floods. Remove soon. XXX */ + } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) { + printk(KERN_NOTICE "%s: Not resetting multi-interface device\n", + sc->name); /* P3 This floods. Remove soon. XXX */ + } else { + if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) { + printk(KERN_NOTICE + "%s: usb_lock_device_for_reset failed (%d)\n", + sc->name, lkr); + } else { + rc = usb_reset_device(sc->dev); + if (rc < 0) { + printk(KERN_NOTICE "%s: " + "usb_lock_device_for_reset failed (%d)\n", + sc->name, rc); + } + + if (lkr) + usb_unlock_device(sc->dev); + } + } + + /* + * In theory, no commands can be running while reset is active, + * so nobody can ask for another reset, and so we do not need any + * queues of resets or anything. We do need a spinlock though, + * to interact with block layer. + */ + spin_lock_irqsave(&sc->lock, flags); + sc->reset = 0; + tasklet_schedule(&sc->tasklet); + list_for_each(p, &sc->luns) { + lun = list_entry(p, struct ub_lun, link); + blk_start_queue(lun->disk->queue); + } + wake_up(&sc->reset_wait); + spin_unlock_irqrestore(&sc->lock, flags); +} + +/* * This is called from a process context. */ static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun) @@ -2146,7 +2300,7 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, if (ep_in == NULL || ep_out == NULL) { printk(KERN_NOTICE "%s: failed endpoint check\n", sc->name); - return -EIO; + return -ENODEV; } /* Calculate and store the pipe values */ @@ -2172,6 +2326,9 @@ static int ub_probe(struct usb_interface *intf, int rc; int i; + if (usb_usual_check_type(dev_id, USB_US_TYPE_UB)) + return -ENXIO; + rc = -ENOMEM; if ((sc = kmalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL) goto err_core; @@ -2181,6 +2338,8 @@ static int ub_probe(struct usb_interface *intf, usb_init_urb(&sc->work_urb); tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc); atomic_set(&sc->poison, 0); + INIT_WORK(&sc->reset_work, ub_reset_task, sc); + init_waitqueue_head(&sc->reset_wait); init_timer(&sc->work_timer); sc->work_timer.data = (unsigned long) sc; @@ -2201,7 +2360,8 @@ static int ub_probe(struct usb_interface *intf, /* XXX Verify that we can handle the device (from descriptors) */ - ub_get_pipes(sc, sc->dev, intf); + if (ub_get_pipes(sc, sc->dev, intf) != 0) + goto err_dev_desc; if (device_create_file(&sc->intf->dev, &dev_attr_diag) != 0) goto err_diag; @@ -2272,6 +2432,7 @@ static int ub_probe(struct usb_interface *intf, /* device_remove_file(&sc->intf->dev, &dev_attr_diag); */ err_diag: +err_dev_desc: usb_set_intfdata(intf, NULL); // usb_put_intf(sc->intf); usb_put_dev(sc->dev); @@ -2309,14 +2470,14 @@ static int ub_probe_lun(struct ub_dev *sc, int lnum) ub_revalidate(sc, lun); rc = -ENOMEM; - if ((disk = alloc_disk(UB_MINORS_PER_MAJOR)) == NULL) + if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL) goto err_diskalloc; lun->disk = disk; sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a'); sprintf(disk->devfs_name, DEVFS_NAME "/%c", lun->id + 'a'); disk->major = UB_MAJOR; - disk->first_minor = lun->id * UB_MINORS_PER_MAJOR; + disk->first_minor = lun->id * UB_PARTS_PER_LUN; disk->fops = &ub_bd_fops; disk->private_data = lun; disk->driverfs_dev = &sc->intf->dev; @@ -2380,6 +2541,11 @@ static void ub_disconnect(struct usb_interface *intf) atomic_set(&sc->poison, 1); /* + * Wait for reset to end, if any. + */ + wait_event(sc->reset_wait, !sc->reset); + + /* * Blow away queued commands. * * Actually, this never works, because before we get here @@ -2392,7 +2558,7 @@ static void ub_disconnect(struct usb_interface *intf) { struct ub_scsi_cmd *cmd; int cnt = 0; - while ((cmd = ub_cmdq_pop(sc)) != NULL) { + while ((cmd = ub_cmdq_peek(sc)) != NULL) { cmd->error = -ENOTCONN; cmd->state = UB_CMDST_DONE; ub_cmdtr_state(sc, cmd); @@ -2461,7 +2627,6 @@ static void ub_disconnect(struct usb_interface *intf) } static struct usb_driver ub_driver = { - .owner = THIS_MODULE, .name = "ub", .probe = ub_probe, .disconnect = ub_disconnect, @@ -2479,6 +2644,7 @@ static int __init ub_init(void) if ((rc = usb_register(&ub_driver)) != 0) goto err_register; + usb_usual_set_present(USB_US_TYPE_UB); return 0; err_register: @@ -2494,6 +2660,7 @@ static void __exit ub_exit(void) devfs_remove(DEVFS_NAME); unregister_blkdev(UB_MAJOR, DRV_NAME); + usb_usual_clear_present(USB_US_TYPE_UB); } module_init(ub_init); diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 8e7fb355177..3e7a067cc08 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -275,7 +275,6 @@ static void bcm203x_disconnect(struct usb_interface *intf) } static struct usb_driver bcm203x_driver = { - .owner = THIS_MODULE, .name = "bcm203x", .probe = bcm203x_probe, .disconnect = bcm203x_disconnect, diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 067e27893e4..8947c8837da 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -768,7 +768,6 @@ static void bfusb_disconnect(struct usb_interface *intf) } static struct usb_driver bfusb_driver = { - .owner = THIS_MODULE, .name = "bfusb", .probe = bfusb_probe, .disconnect = bfusb_disconnect, diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 394796315ad..9446960ac74 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -619,7 +619,6 @@ static void bpa10x_disconnect(struct usb_interface *intf) } static struct usb_driver bpa10x_driver = { - .owner = THIS_MODULE, .name = "bpa10x", .probe = bpa10x_probe, .disconnect = bpa10x_disconnect, diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c index 057cb2b6e6d..92382e82328 100644 --- a/drivers/bluetooth/hci_usb.c +++ b/drivers/bluetooth/hci_usb.c @@ -1044,7 +1044,6 @@ static void hci_usb_disconnect(struct usb_interface *intf) } static struct usb_driver hci_usb_driver = { - .owner = THIS_MODULE, .name = "hci_usb", .probe = hci_usb_probe, .disconnect = hci_usb_disconnect, diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c index 092e9b13375..1533f56baa4 100644 --- a/drivers/char/watchdog/pcwd_usb.c +++ b/drivers/char/watchdog/pcwd_usb.c @@ -151,7 +151,6 @@ static void usb_pcwd_disconnect (struct usb_interface *interface); /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver usb_pcwd_driver = { - .owner = THIS_MODULE, .name = DRIVER_NAME, .probe = usb_pcwd_probe, .disconnect = usb_pcwd_disconnect, diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c index 64b4a308098..bc2fce60f9f 100644 --- a/drivers/input/joystick/iforce/iforce-usb.c +++ b/drivers/input/joystick/iforce/iforce-usb.c @@ -235,7 +235,6 @@ static struct usb_device_id iforce_usb_ids [] = { MODULE_DEVICE_TABLE (usb, iforce_usb_ids); struct usb_driver iforce_usb_driver = { - .owner = THIS_MODULE, .name = "iforce", .probe = iforce_usb_probe, .disconnect = iforce_usb_disconnect, diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c index f8457ef4882..ca5b4a3b683 100644 --- a/drivers/isdn/hisax/hfc_usb.c +++ b/drivers/isdn/hisax/hfc_usb.c @@ -1715,7 +1715,6 @@ hfc_usb_disconnect(struct usb_interface /* our driver information structure */ /************************************/ static struct usb_driver hfc_drv = { - .owner = THIS_MODULE, .name = "hfc_usb", .id_table = hfcusb_idtab, .probe = hfc_usb_probe, diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c index 8e192a3a349..99cb0f3d59a 100644 --- a/drivers/isdn/hisax/st5481_init.c +++ b/drivers/isdn/hisax/st5481_init.c @@ -180,7 +180,6 @@ static struct usb_device_id st5481_ids[] = { MODULE_DEVICE_TABLE (usb, st5481_ids); static struct usb_driver st5481_usb_driver = { - .owner = THIS_MODULE, .name = "st5481_usb", .probe = probe_st5481, .disconnect = disconnect_st5481, diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c index 0a78ba3737a..a6c91db40ad 100644 --- a/drivers/media/dvb/b2c2/flexcop-usb.c +++ b/drivers/media/dvb/b2c2/flexcop-usb.c @@ -544,7 +544,6 @@ static struct usb_device_id flexcop_usb_table [] = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver flexcop_usb_driver = { - .owner = THIS_MODULE, .name = "b2c2_flexcop_usb", .probe = flexcop_usb_probe, .disconnect = flexcop_usb_disconnect, diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index 336fc284fa5..b996fb59b7e 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -986,7 +986,6 @@ static const struct usb_device_id cinergyt2_table [] __devinitdata = { MODULE_DEVICE_TABLE(usb, cinergyt2_table); static struct usb_driver cinergyt2_driver = { - .owner = THIS_MODULE, .name = "cinergyT2", .probe = cinergyt2_probe, .disconnect = cinergyt2_disconnect, diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c index 8c7beffb045..ce44aa6bbb8 100644 --- a/drivers/media/dvb/dvb-usb/a800.c +++ b/drivers/media/dvb/dvb-usb/a800.c @@ -144,7 +144,6 @@ static struct dvb_usb_properties a800_properties = { }; static struct usb_driver a800_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_a800", .probe = a800_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 3fe383f4bb4..d05fab01ccc 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -241,7 +241,6 @@ static struct dvb_usb_properties cxusb_properties = { }; static struct usb_driver cxusb_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_cxusb", .probe = cxusb_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index aa271a2496d..52ac3e5adf5 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -373,7 +373,6 @@ static struct dvb_usb_properties artec_t1_usb2_properties = { }; static struct usb_driver dibusb_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_dibusb_mb", .probe = dibusb_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c index 6a0912eab39..55802fba3c2 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mc.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c @@ -82,7 +82,6 @@ static struct dvb_usb_properties dibusb_mc_properties = { }; static struct usb_driver dibusb_mc_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_dibusb_mc", .probe = dibusb_mc_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index f98e306a575..450417a9e64 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -233,7 +233,6 @@ static struct dvb_usb_properties digitv_properties = { }; static struct usb_driver digitv_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_digitv", .probe = digitv_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index b595476332c..6e2bac87344 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -198,7 +198,6 @@ static struct dvb_usb_properties wt220u_properties = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver dtt200u_usb_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_dtt200u", .probe = dtt200u_usb_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c index 1841a66427b..fac48fc7a4a 100644 --- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c +++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c @@ -202,7 +202,6 @@ static struct dvb_usb_properties nova_t_properties = { }; static struct usb_driver nova_t_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_nova_t_usb2", .probe = nova_t_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index 6fd67657c26..14f1911c79b 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -128,7 +128,6 @@ static struct dvb_usb_properties umt_properties = { }; static struct usb_driver umt_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_umt_010", .probe = umt_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/vp702x.c b/drivers/media/dvb/dvb-usb/vp702x.c index de13c04e8e6..afa00fdb5ec 100644 --- a/drivers/media/dvb/dvb-usb/vp702x.c +++ b/drivers/media/dvb/dvb-usb/vp702x.c @@ -256,7 +256,6 @@ static struct dvb_usb_properties vp702x_properties = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver vp702x_usb_driver = { - .owner = THIS_MODULE, .name = "dvb-usb-vp702x", .probe = vp702x_usb_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/dvb/dvb-usb/vp7045.c b/drivers/media/dvb/dvb-usb/vp7045.c index 75765e3a569..3835235b68d 100644 --- a/drivers/media/dvb/dvb-usb/vp7045.c +++ b/drivers/media/dvb/dvb-usb/vp7045.c @@ -253,7 +253,6 @@ static struct dvb_usb_properties vp7045_properties = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver vp7045_usb_driver = { - .owner = THIS_MODULE, .name = "dvb_usb_vp7045", .probe = vp7045_usb_probe, .disconnect = dvb_usb_device_exit, diff --git a/drivers/media/video/cpia_usb.c b/drivers/media/video/cpia_usb.c index 9774e94d1e7..1439cb75287 100644 --- a/drivers/media/video/cpia_usb.c +++ b/drivers/media/video/cpia_usb.c @@ -582,7 +582,6 @@ MODULE_LICENSE("GPL"); static struct usb_driver cpia_driver = { - .owner = THIS_MODULE, .name = "cpia", .probe = cpia_probe, .disconnect = cpia_disconnect, diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 06d76879bde..3a56120397a 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1884,7 +1884,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) } static struct usb_driver em28xx_usb_driver = { - .owner = THIS_MODULE, .name = "em28xx", .probe = em28xx_usb_probe, .disconnect = em28xx_usb_disconnect, diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index c22c0517883..fa176ffb4ad 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -1539,7 +1539,6 @@ static void irda_usb_disconnect(struct usb_interface *intf) * USB device callbacks */ static struct usb_driver irda_driver = { - .owner = THIS_MODULE, .name = "irda-usb", .probe = irda_usb_probe, .disconnect = irda_usb_disconnect, diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 3961a754e92..31867e4b891 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -1152,7 +1152,6 @@ static int stir_resume(struct usb_interface *intf) * USB device callbacks */ static struct usb_driver irda_driver = { - .owner = THIS_MODULE, .name = "stir4200", .probe = stir_probe, .disconnect = stir_disconnect, diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index a50c2bc506f..3639c3f8d35 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_MIDI) += class/ obj-$(CONFIG_USB_PRINTER) += class/ obj-$(CONFIG_USB_STORAGE) += storage/ +obj-$(CONFIG_USB) += storage/ obj-$(CONFIG_USB_AIPTEK) += input/ obj-$(CONFIG_USB_ATI_REMOTE) += input/ diff --git a/drivers/usb/atm/Kconfig b/drivers/usb/atm/Kconfig index f429862e097..550ddfa71a4 100644 --- a/drivers/usb/atm/Kconfig +++ b/drivers/usb/atm/Kconfig @@ -44,6 +44,19 @@ config USB_CXACRU To compile this driver as a module, choose M here: the module will be called cxacru. +config USB_UEAGLEATM + tristate "ADI 930 and eagle USB DSL modem" + depends on USB_ATM + select FW_LOADER + help + Say Y here if you have an ADSL USB modem based on the ADI 930 + or eagle chipset. In order to use your modem you will need to + install firmwares and CMV (Command Management Variables); see + <https://gna.org/projects/ueagleatm/> for details. + + To compile this driver as a module, choose M here: the + module will be called ueagle-atm. + config USB_XUSBATM tristate "Other USB DSL modem support" depends on USB_ATM diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile index 85099718c68..4c4a776ab1c 100644 --- a/drivers/usb/atm/Makefile +++ b/drivers/usb/atm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_USB_CXACRU) += cxacru.o obj-$(CONFIG_USB_SPEEDTOUCH) += speedtch.o +obj-$(CONFIG_USB_UEAGLEATM) += ueagle-atm.o obj-$(CONFIG_USB_ATM) += usbatm.o obj-$(CONFIG_USB_XUSBATM) += xusbatm.o diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 9d59dc62e6d..af0a41e7870 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -853,7 +853,6 @@ static int cxacru_usb_probe(struct usb_interface *intf, const struct usb_device_ } static struct usb_driver cxacru_usb_driver = { - .owner = THIS_MODULE, .name = cxacru_driver_name, .probe = cxacru_usb_probe, .disconnect = usbatm_usb_disconnect, diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index d0cbbb7f038..b2833614865 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -659,7 +659,6 @@ MODULE_DEVICE_TABLE(usb, speedtch_usb_ids); static int speedtch_usb_probe(struct usb_interface *, const struct usb_device_id *); static struct usb_driver speedtch_usb_driver = { - .owner = THIS_MODULE, .name = speedtch_driver_name, .probe = speedtch_usb_probe, .disconnect = usbatm_usb_disconnect, diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c new file mode 100644 index 00000000000..7d2a679989e --- /dev/null +++ b/drivers/usb/atm/ueagle-atm.c @@ -0,0 +1,1820 @@ +/*- + * Copyright (c) 2003, 2004 + * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. + * + * Copyright (c) 2005 Matthieu Castet <castet.matthieu@free.fr> + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * BSD license below: + * + * 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 unmodified, 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 OR CONTRIBUTORS 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. + * + * GPL license : + * 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 Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * HISTORY : some part of the code was base on ueagle 1.3 BSD driver, + * Damien Bergamini agree to put his code under a DUAL GPL/BSD license. + * + * The rest of the code was was rewritten from scratch. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/crc32.h> +#include <linux/usb.h> +#include <linux/firmware.h> +#include <linux/ctype.h> +#include <linux/kthread.h> +#include <linux/version.h> +#include <asm/unaligned.h> + +#include "usbatm.h" + +#define EAGLEUSBVERSION "ueagle 1.1" + + +/* + * Debug macros + */ +#define uea_dbg(usb_dev, format, args...) \ + do { \ + if (debug >= 1) \ + dev_dbg(&(usb_dev)->dev, \ + "[ueagle-atm dbg] %s: " format, \ + __FUNCTION__, ##args); \ + } while (0) + +#define uea_vdbg(usb_dev, format, args...) \ + do { \ + if (debug >= 2) \ + dev_dbg(&(usb_dev)->dev, \ + "[ueagle-atm vdbg] " format, ##args); \ + } while (0) + +#define uea_enters(usb_dev) \ + uea_vdbg(usb_dev, "entering %s\n", __FUNCTION__) + +#define uea_leaves(usb_dev) \ + uea_vdbg(usb_dev, "leaving %s\n", __FUNCTION__) + +#define uea_err(usb_dev, format,args...) \ + dev_err(&(usb_dev)->dev ,"[UEAGLE-ATM] " format , ##args) + +#define uea_warn(usb_dev, format,args...) \ + dev_warn(&(usb_dev)->dev ,"[Ueagle-atm] " format, ##args) + +#define uea_info(usb_dev, format,args...) \ + dev_info(&(usb_dev)->dev ,"[ueagle-atm] " format, ##args) + +struct uea_cmvs { + u32 address; + u16 offset; + u32 data; +} __attribute__ ((packed)); + +struct uea_softc { + struct usb_device *usb_dev; + struct usbatm_data *usbatm; + + int modem_index; + unsigned int driver_info; + + int booting; + int reset; + + wait_queue_head_t sync_q; + + struct task_struct *kthread; + u32 data; + wait_queue_head_t cmv_ack_wait; + int cmv_ack; + + struct work_struct task; + u16 pageno; + u16 ovl; + + const struct firmware *dsp_firm; + struct urb *urb_int; + + u8 cmv_function; + u16 cmv_idx; + u32 cmv_address; + u16 cmv_offset; + + /* keep in sync with eaglectl */ + struct uea_stats { + struct { + u32 state; + u32 flags; + u32 mflags; + u32 vidcpe; + u32 vidco; + u32 dsrate; + u32 usrate; + u32 dsunc; + u32 usunc; + u32 dscorr; + u32 uscorr; + u32 txflow; + u32 rxflow; + u32 usattenuation; + u32 dsattenuation; + u32 dsmargin; + u32 usmargin; + u32 firmid; + } phy; + } stats; +}; + +/* + * Elsa IDs + */ +#define ELSA_VID 0x05CC +#define ELSA_PID_PSTFIRM 0x3350 +#define ELSA_PID_PREFIRM 0x3351 + +/* + * Sagem USB IDs + */ +#define EAGLE_VID 0x1110 +#define EAGLE_I_PID_PREFIRM 0x9010 /* Eagle I */ +#define EAGLE_I_PID_PSTFIRM 0x900F /* Eagle I */ + +#define EAGLE_IIC_PID_PREFIRM 0x9024 /* Eagle IIC */ +#define EAGLE_IIC_PID_PSTFIRM 0x9023 /* Eagle IIC */ + +#define EAGLE_II_PID_PREFIRM 0x9022 /* Eagle II */ +#define EAGLE_II_PID_PSTFIRM 0x9021 /* Eagle II */ + +/* + * Eagle III Pid + */ +#define EAGLE_III_PID_PREFIRM 0x9032 /* Eagle III */ +#define EAGLE_III_PID_PSTFIRM 0x9031 /* Eagle III */ + +/* + * USR USB IDs + */ +#define USR_VID 0x0BAF +#define MILLER_A_PID_PREFIRM 0x00F2 +#define MILLER_A_PID_PSTFIRM 0x00F1 +#define MILLER_B_PID_PREFIRM 0x00FA +#define MILLER_B_PID_PSTFIRM 0x00F9 +#define HEINEKEN_A_PID_PREFIRM 0x00F6 +#define HEINEKEN_A_PID_PSTFIRM 0x00F5 +#define HEINEKEN_B_PID_PREFIRM 0x00F8 +#define HEINEKEN_B_PID_PSTFIRM 0x00F7 + +#define PREFIRM 0 +#define PSTFIRM (1<<7) +enum { + ADI930 = 0, + EAGLE_I, + EAGLE_II, + EAGLE_III +}; + +/* macros for both struct usb_device_id and struct uea_softc */ +#define UEA_IS_PREFIRM(x) \ + (!((x)->driver_info & PSTFIRM)) +#define UEA_CHIP_VERSION(x) \ + ((x)->driver_info & 0xf) + +#define IS_ISDN(sc) \ + (le16_to_cpu(sc->usb_dev->descriptor.bcdDevice) & 0x80) + +#define INS_TO_USBDEV(ins) ins->usb_dev + +#define GET_STATUS(data) \ + ((data >> 8) & 0xf) +#define IS_OPERATIONAL(sc) \ + (GET_STATUS(sc->stats.phy.state) == 2) + +/* + * Set of macros to handle unaligned data in the firmware blob. + * The FW_GET_BYTE() macro is provided only for consistency. + */ + +#define FW_GET_BYTE(p) *((__u8 *) (p)) +#define FW_GET_WORD(p) le16_to_cpu(get_unaligned((__le16 *) (p))) +#define FW_GET_LONG(p) le32_to_cpu(get_unaligned((__le32 *) (p))) + +#define FW_DIR "ueagle-atm/" +#define NB_MODEM 4 + +#define BULK_TIMEOUT 300 +#define CTRL_TIMEOUT 1000 + +#define ACK_TIMEOUT msecs_to_jiffies(1500) + +#define UEA_INTR_IFACE_NO 0 +#define UEA_US_IFACE_NO 1 +#define UEA_DS_IFACE_NO 2 + +#define FASTEST_ISO_INTF 8 + +#define UEA_BULK_DATA_PIPE 0x02 +#define UEA_IDMA_PIPE 0x04 +#define UEA_INTR_PIPE 0x04 +#define UEA_ISO_DATA_PIPE 0x08 + +#define UEA_SET_BLOCK 0x0001 +#define UEA_SET_MODE 0x0003 +#define UEA_SET_2183_DATA 0x0004 +#define UEA_SET_TIMEOUT 0x0011 + +#define UEA_LOOPBACK_OFF 0x0002 +#define UEA_LOOPBACK_ON 0x0003 +#define UEA_BOOT_IDMA 0x0006 +#define UEA_START_RESET 0x0007 +#define UEA_END_RESET 0x0008 + +#define UEA_SWAP_MAILBOX (0x3fcd | 0x4000) +#define UEA_MPTX_START (0x3fce | 0x4000) +#define UEA_MPTX_MAILBOX (0x3fd6 | 0x4000) +#define UEA_MPRX_MAILBOX (0x3fdf | 0x4000) + +/* structure describing a block within a DSP page */ +struct block_info { + __le16 wHdr; +#define UEA_BIHDR 0xabcd + __le16 wAddress; + __le16 wSize; + __le16 wOvlOffset; + __le16 wOvl; /* overlay */ + __le16 wLast; +} __attribute__ ((packed)); +#define BLOCK_INFO_SIZE 12 + +/* structure representing a CMV (Configuration and Management Variable) */ +struct cmv { + __le16 wPreamble; +#define PREAMBLE 0x535c + __u8 bDirection; +#define MODEMTOHOST 0x01 +#define HOSTTOMODEM 0x10 + __u8 bFunction; +#define FUNCTION_TYPE(f) ((f) >> 4) +#define MEMACCESS 0x1 +#define ADSLDIRECTIVE 0x7 + +#define FUNCTION_SUBTYPE(f) ((f) & 0x0f) +/* for MEMACCESS */ +#define REQUESTREAD 0x0 +#define REQUESTWRITE 0x1 +#define REPLYREAD 0x2 +#define REPLYWRITE 0x3 +/* for ADSLDIRECTIVE */ +#define KERNELREADY 0x0 +#define MODEMREADY 0x1 + +#define MAKEFUNCTION(t, s) (((t) & 0xf) << 4 | ((s) & 0xf)) + __le16 wIndex; + __le32 dwSymbolicAddress; +#define MAKESA(a, b, c, d) \ + (((c) & 0xff) << 24 | \ + ((d) & 0xff) << 16 | \ + ((a) & 0xff) << 8 | \ + ((b) & 0xff)) + +#define SA_CNTL MAKESA('C', 'N', 'T', 'L') +#define SA_DIAG MAKESA('D', 'I', 'A', 'G') +#define SA_INFO MAKESA('I', 'N', 'F', 'O') +#define SA_OPTN MAKESA('O', 'P', 'T', 'N') +#define SA_RATE MAKESA('R', 'A', 'T', 'E') +#define SA_STAT MAKESA('S', 'T', 'A', 'T') + __le16 wOffsetAddress; + __le32 dwData; +} __attribute__ ((packed)); +#define CMV_SIZE 16 + +/* structure representing swap information */ +struct swap_info { + __u8 bSwapPageNo; + __u8 bOvl; /* overlay */ +} __attribute__ ((packed)); + +/* structure representing interrupt data */ +struct intr_pkt { + __u8 bType; + __u8 bNotification; + __le16 wValue; + __le16 wIndex; + __le16 wLength; + __le16 wInterrupt; +#define INT_LOADSWAPPAGE 0x0001 +#define INT_INCOMINGCMV 0x0002 + union { + struct { + struct swap_info swapinfo; + __le16 wDataSize; + } __attribute__ ((packed)) s1; + + struct { + struct cmv cmv; + __le16 wDataSize; + } __attribute__ ((packed)) s2; + } __attribute__ ((packed)) u; +#define bSwapPageNo u.s1.swapinfo.bSwapPageNo +#define bOvl u.s1.swapinfo.bOvl +} __attribute__ ((packed)); +#define INTR_PKT_SIZE 28 + +static struct usb_driver uea_driver; +static DECLARE_MUTEX(uea_semaphore); +static const char *chip_name[] = {"ADI930", "Eagle I", "Eagle II", "Eagle III"}; + +static int modem_index; +static unsigned int debug; +static int sync_wait[NB_MODEM]; +static char *cmv_file[NB_MODEM]; + +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "module debug level (0=off,1=on,2=verbose)"); +module_param_array(sync_wait, bool, NULL, 0644); +MODULE_PARM_DESC(sync_wait, "wait the synchronisation before starting ATM"); +module_param_array(cmv_file, charp, NULL, 0644); +MODULE_PARM_DESC(cmv_file, + "file name with configuration and management variables"); + +#define UPDATE_ATM_STAT(type, val) \ + do { \ + if (sc->usbatm->atm_dev) \ + sc->usbatm->atm_dev->type = val; \ + } while (0) + +/* Firmware loading */ +#define LOAD_INTERNAL 0xA0 +#define F8051_USBCS 0x7f92 + +/** + * uea_send_modem_cmd - Send a command for pre-firmware devices. + */ +static int uea_send_modem_cmd(struct usb_device *usb, + u16 addr, u16 size, u8 * buff) +{ + int ret = -ENOMEM; + u8 *xfer_buff; + + xfer_buff = kmalloc(size, GFP_KERNEL); + if (xfer_buff) { + memcpy(xfer_buff, buff, size); + ret = usb_control_msg(usb, + usb_sndctrlpipe(usb, 0), + LOAD_INTERNAL, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, addr, 0, xfer_buff, + size, CTRL_TIMEOUT); + kfree(xfer_buff); + } + + if (ret < 0) + return ret; + + return (ret == size) ? 0 : -EIO; +} + +static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context) +{ + struct usb_device *usb = context; + u8 *pfw, value; + u32 crc = 0; + int ret, size; + + uea_enters(usb); + if (!fw_entry) { + uea_err(usb, "firmware is not available\n"); + goto err; + } + + pfw = fw_entry->data; + size = fw_entry->size; + if (size < 4) + goto err_fw_corrupted; + + crc = FW_GET_LONG(pfw); + pfw += 4; + size -= 4; + if (crc32_be(0, pfw, size) != crc) + goto err_fw_corrupted; + + /* + * Start to upload formware : send reset + */ + value = 1; + ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value); + + if (ret < 0) { + uea_err(usb, "modem reset failed with error %d\n", ret); + goto err; + } + + while (size > 3) { + u8 len = FW_GET_BYTE(pfw); + u16 add = FW_GET_WORD(pfw + 1); + + size -= len + 3; + if (size < 0) + goto err_fw_corrupted; + + ret = uea_send_modem_cmd(usb, add, len, pfw + 3); + if (ret < 0) { + uea_err(usb, "uploading firmware data failed " + "with error %d\n", ret); + goto err; + } + pfw += len + 3; + } + + if (size != 0) + goto err_fw_corrupted; + + /* + * Tell the modem we finish : de-assert reset + */ + value = 0; + ret = uea_send_modem_cmd(usb, F8051_USBCS, 1, &value); + if (ret < 0) + uea_err(usb, "modem de-assert failed with error %d\n", ret); + else + uea_info(usb, "firmware uploaded\n"); + + uea_leaves(usb); + return; + +err_fw_corrupted: + uea_err(usb, "firmware is corrupted\n"); +err: + uea_leaves(usb); +} + +/** + * uea_load_firmware - Load usb firmware for pre-firmware devices. + */ +static int uea_load_firmware(struct usb_device *usb, unsigned int ver) +{ + int ret; + char *fw_name = FW_DIR "eagle.fw"; + + uea_enters(usb); + uea_info(usb, "pre-firmware device, uploading firmware\n"); + + switch (ver) { + case ADI930: + fw_name = FW_DIR "adi930.fw"; + break; + case EAGLE_I: + fw_name = FW_DIR "eagleI.fw"; + break; + case EAGLE_II: + fw_name = FW_DIR "eagleII.fw"; + break; + case EAGLE_III: + fw_name = FW_DIR "eagleIII.fw"; + break; + } + + ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev, usb, uea_upload_pre_firmware); + if (ret) + uea_err(usb, "firmware %s is not available\n", fw_name); + else + uea_info(usb, "loading firmware %s\n", fw_name); + + uea_leaves(usb); + return ret; +} + +/* modem management : dsp firmware, send/read CMV, monitoring statistic + */ + +/* + * Make sure that the DSP code provided is safe to use. + */ +static int check_dsp(u8 *dsp, unsigned int len) +{ + u8 pagecount, blockcount; + u16 blocksize; + u32 pageoffset; + unsigned int i, j, p, pp; + + pagecount = FW_GET_BYTE(dsp); + p = 1; + + /* enough space for page offsets? */ + if (p + 4 * pagecount > len) + return 1; + + for (i = 0; i < pagecount; i++) { + + pageoffset = FW_GET_LONG(dsp + p); + p += 4; + + if (pageoffset == 0) + continue; + + /* enough space for blockcount? */ + if (pageoffset >= len) + return 1; + + pp = pageoffset; + blockcount = FW_GET_BYTE(dsp + pp); + pp += 1; + + for (j = 0; j < blockcount; j++) { + + /* enough space for block header? */ + if (pp + 4 > len) + return 1; + + pp += 2; /* skip blockaddr */ + blocksize = FW_GET_WORD(dsp + pp); + pp += 2; + + /* enough space for block data? */ + if (pp + blocksize > len) + return 1; + + pp += blocksize; + } + } + + return 0; +} + +/* + * send data to the idma pipe + * */ +static int uea_idma_write(struct uea_softc *sc, void *data, u32 size) +{ + int ret = -ENOMEM; + u8 *xfer_buff; + int bytes_read; + + xfer_buff = kmalloc(size, GFP_KERNEL); + if (!xfer_buff) { + uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n"); + return ret; + } + + memcpy(xfer_buff, data, size); + + ret = usb_bulk_msg(sc->usb_dev, + usb_sndbulkpipe(sc->usb_dev, UEA_IDMA_PIPE), + xfer_buff, size, &bytes_read, BULK_TIMEOUT); + + kfree(xfer_buff); + if (ret < 0) + return ret; + if (size != bytes_read) { + uea_err(INS_TO_USBDEV(sc), "size != bytes_read %d %d\n", size, + bytes_read); + return -EIO; + } + + return 0; +} + +static int request_dsp(struct uea_softc *sc) +{ + int ret; + char *dsp_name; + + if (UEA_CHIP_VERSION(sc) == ADI930) { + if (IS_ISDN(sc)) + dsp_name = FW_DIR "DSP9i.bin"; + else + dsp_name = FW_DIR "DSP9p.bin"; + } else { + if (IS_ISDN(sc)) + dsp_name = FW_DIR "DSPei.bin"; + else + dsp_name = FW_DIR "DSPep.bin"; + } + + ret = request_firmware(&sc->dsp_firm, + dsp_name, &sc->usb_dev->dev); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "requesting firmware %s failed with error %d\n", + dsp_name, ret); + return ret; + } + + if (check_dsp(sc->dsp_firm->data, sc->dsp_firm->size)) { + uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", + dsp_name); + release_firmware(sc->dsp_firm); + sc->dsp_firm = NULL; + return -EILSEQ; + } + + return 0; +} + +/* + * The uea_load_page() function must be called within a process context + */ +static void uea_load_page(void *xsc) +{ + struct uea_softc *sc = xsc; + u16 pageno = sc->pageno; + u16 ovl = sc->ovl; + struct block_info bi; + + u8 *p; + u8 pagecount, blockcount; + u16 blockaddr, blocksize; + u32 pageoffset; + int i; + + /* reload firmware when reboot start and it's loaded already */ + if (ovl == 0 && pageno == 0 && sc->dsp_firm) { + release_firmware(sc->dsp_firm); + sc->dsp_firm = NULL; + } + + if (sc->dsp_firm == NULL && request_dsp(sc) < 0) + return; + + p = sc->dsp_firm->data; + pagecount = FW_GET_BYTE(p); + p += 1; + + if (pageno >= pagecount) + goto bad1; + + p += 4 * pageno; + pageoffset = FW_GET_LONG(p); + + if (pageoffset == 0) + goto bad1; + + p = sc->dsp_firm->data + pageoffset; + blockcount = FW_GET_BYTE(p); + p += 1; + + uea_dbg(INS_TO_USBDEV(sc), + "sending %u blocks for DSP page %u\n", blockcount, pageno); + + bi.wHdr = cpu_to_le16(UEA_BIHDR); + bi.wOvl = cpu_to_le16(ovl); + bi.wOvlOffset = cpu_to_le16(ovl | 0x8000); + + for (i = 0; i < blockcount; i++) { + blockaddr = FW_GET_WORD(p); + p += 2; + + blocksize = FW_GET_WORD(p); + p += 2; + + bi.wSize = cpu_to_le16(blocksize); + bi.wAddress = cpu_to_le16(blockaddr); + bi.wLast = cpu_to_le16((i == blockcount - 1) ? 1 : 0); + + /* send block info through the IDMA pipe */ + if (uea_idma_write(sc, &bi, BLOCK_INFO_SIZE)) + goto bad2; + + /* send block data through the IDMA pipe */ + if (uea_idma_write(sc, p, blocksize)) + goto bad2; + + p += blocksize; + } + + return; + +bad2: + uea_err(INS_TO_USBDEV(sc), "sending DSP block %u failed\n", i); + return; +bad1: + uea_err(INS_TO_USBDEV(sc), "invalid DSP page %u requested\n",pageno); +} + +static inline void wake_up_cmv_ack(struct uea_softc *sc) +{ + sc->cmv_ack = 1; + wake_up(&sc->cmv_ack_wait); +} + +static inline int wait_cmv_ack(struct uea_softc *sc) +{ + int ret = wait_event_timeout(sc->cmv_ack_wait, + sc->cmv_ack, ACK_TIMEOUT); + sc->cmv_ack = 0; + + if (ret < 0) + return ret; + + return (ret == 0) ? -ETIMEDOUT : 0; + +} + +#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00 + +static int uea_request(struct uea_softc *sc, + u16 value, u16 index, u16 size, void *data) +{ + u8 *xfer_buff; + int ret = -ENOMEM; + + xfer_buff = kmalloc(size, GFP_KERNEL); + if (!xfer_buff) { + uea_err(INS_TO_USBDEV(sc), "can't allocate xfer_buff\n"); + return ret; + } + memcpy(xfer_buff, data, size); + + ret = usb_control_msg(sc->usb_dev, usb_sndctrlpipe(sc->usb_dev, 0), + UCDC_SEND_ENCAPSULATED_COMMAND, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, xfer_buff, size, CTRL_TIMEOUT); + + kfree(xfer_buff); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), "usb_control_msg error %d\n", ret); + return ret; + } + + if (ret != size) { + uea_err(INS_TO_USBDEV(sc), + "usb_control_msg send only %d bytes (instead of %d)\n", + ret, size); + return -EIO; + } + + return 0; +} + +static int uea_cmv(struct uea_softc *sc, + u8 function, u32 address, u16 offset, u32 data) +{ + struct cmv cmv; + int ret; + + /* we send a request, but we expect a reply */ + sc->cmv_function = function | 0x2; + sc->cmv_idx++; + sc->cmv_address = address; + sc->cmv_offset = offset; + + cmv.wPreamble = cpu_to_le16(PREAMBLE); + cmv.bDirection = HOSTTOMODEM; + cmv.bFunction = function; + cmv.wIndex = cpu_to_le16(sc->cmv_idx); + put_unaligned(cpu_to_le32(address), &cmv.dwSymbolicAddress); + cmv.wOffsetAddress = cpu_to_le16(offset); + put_unaligned(cpu_to_le32(data >> 16 | data << 16), &cmv.dwData); + + ret = uea_request(sc, UEA_SET_BLOCK, UEA_MPTX_START, CMV_SIZE, &cmv); + if (ret < 0) + return ret; + return wait_cmv_ack(sc); +} + +static inline int uea_read_cmv(struct uea_softc *sc, + u32 address, u16 offset, u32 *data) +{ + int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTREAD), + address, offset, 0); + if (ret < 0) + uea_err(INS_TO_USBDEV(sc), + "reading cmv failed with error %d\n", ret); + else + *data = sc->data; + + return ret; +} + +static inline int uea_write_cmv(struct uea_softc *sc, + u32 address, u16 offset, u32 data) +{ + int ret = uea_cmv(sc, MAKEFUNCTION(MEMACCESS, REQUESTWRITE), + address, offset, data); + if (ret < 0) + uea_err(INS_TO_USBDEV(sc), + "writing cmv failed with error %d\n", ret); + + return ret; +} + +/* + * Monitor the modem and update the stat + * return 0 if everything is ok + * return < 0 if an error occurs (-EAGAIN reboot needed) + */ +static int uea_stat(struct uea_softc *sc) +{ + u32 data; + int ret; + + uea_enters(INS_TO_USBDEV(sc)); + data = sc->stats.phy.state; + + ret = uea_read_cmv(sc, SA_STAT, 0, &sc->stats.phy.state); + if (ret < 0) + return ret; + + switch (GET_STATUS(sc->stats.phy.state)) { + case 0: /* not yet synchronized */ + uea_dbg(INS_TO_USBDEV(sc), + "modem not yet synchronized\n"); + return 0; + + case 1: /* initialization */ + uea_dbg(INS_TO_USBDEV(sc), "modem initializing\n"); + return 0; + + case 2: /* operational */ + uea_vdbg(INS_TO_USBDEV(sc), "modem operational\n"); + break; + + case 3: /* fail ... */ + uea_info(INS_TO_USBDEV(sc), "modem synchronization failed\n"); + return -EAGAIN; + + case 4 ... 6: /* test state */ + uea_warn(INS_TO_USBDEV(sc), + "modem in test mode - not supported\n"); + return -EAGAIN; + + case 7: /* fast-retain ... */ + uea_info(INS_TO_USBDEV(sc), "modem in fast-retain mode\n"); + return 0; + default: + uea_err(INS_TO_USBDEV(sc), "modem invalid SW mode %d\n", + GET_STATUS(sc->stats.phy.state)); + return -EAGAIN; + } + + if (GET_STATUS(data) != 2) { + uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_OFF, 0, NULL); + uea_info(INS_TO_USBDEV(sc), "modem operational\n"); + + /* release the dsp firmware as it is not needed until + * the next failure + */ + if (sc->dsp_firm) { + release_firmware(sc->dsp_firm); + sc->dsp_firm = NULL; + } + + ret = uea_read_cmv(sc, SA_INFO, 10, &sc->stats.phy.firmid); + if (ret < 0) + return ret; + uea_info(INS_TO_USBDEV(sc), "ATU-R firmware version : %x\n", + sc->stats.phy.firmid); + } + + /* always update it as atm layer could not be init when we switch to + * operational state + */ + UPDATE_ATM_STAT(signal, ATM_PHY_SIG_FOUND); + + /* wake up processes waiting for synchronization */ + wake_up(&sc->sync_q); + + ret = uea_read_cmv(sc, SA_DIAG, 2, &sc->stats.phy.flags); + if (ret < 0) + return ret; + sc->stats.phy.mflags |= sc->stats.phy.flags; + + /* in case of a flags ( for example delineation LOSS (& 0x10)), + * we check the status again in order to detect the failure earlier + */ + if (sc->stats.phy.flags) { + uea_dbg(INS_TO_USBDEV(sc), "Stat flag = %d\n", + sc->stats.phy.flags); + return 0; + } + + ret = uea_read_cmv(sc, SA_RATE, 0, &data); + if (ret < 0) + return ret; + + /* in bulk mode the modem have problem with high rate + * changing internal timing could improve things, but the + * value is misterious. + * ADI930 don't support it (-EPIPE error). + */ + if (UEA_CHIP_VERSION(sc) != ADI930 + && sc->stats.phy.dsrate != (data >> 16) * 32) { + /* Original timming from ADI(used in windows driver) + * 0x20ffff>>16 * 32 = 32 * 32 = 1Mbits + */ + u16 timeout = (data <= 0x20ffff) ? 0 : 1; + ret = uea_request(sc, UEA_SET_TIMEOUT, timeout, 0, NULL); + uea_info(INS_TO_USBDEV(sc), + "setting new timeout %d%s\n", timeout, + ret < 0?" failed":""); + } + sc->stats.phy.dsrate = (data >> 16) * 32; + sc->stats.phy.usrate = (data & 0xffff) * 32; + UPDATE_ATM_STAT(link_rate, sc->stats.phy.dsrate * 1000 / 424); + + ret = uea_read_cmv(sc, SA_DIAG, 23, &data); + if (ret < 0) + return ret; + sc->stats.phy.dsattenuation = (data & 0xff) / 2; + + ret = uea_read_cmv(sc, SA_DIAG, 47, &data); + if (ret < 0) + return ret; + sc->stats.phy.usattenuation = (data & 0xff) / 2; + + ret = uea_read_cmv(sc, SA_DIAG, 25, &sc->stats.phy.dsmargin); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_DIAG, 49, &sc->stats.phy.usmargin); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_DIAG, 51, &sc->stats.phy.rxflow); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_DIAG, 52, &sc->stats.phy.txflow); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_DIAG, 54, &sc->stats.phy.dsunc); + if (ret < 0) + return ret; + + /* only for atu-c */ + ret = uea_read_cmv(sc, SA_DIAG, 58, &sc->stats.phy.usunc); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_DIAG, 53, &sc->stats.phy.dscorr); + if (ret < 0) + return ret; + + /* only for atu-c */ + ret = uea_read_cmv(sc, SA_DIAG, 57, &sc->stats.phy.uscorr); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_INFO, 8, &sc->stats.phy.vidco); + if (ret < 0) + return ret; + + ret = uea_read_cmv(sc, SA_INFO, 13, &sc->stats.phy.vidcpe); + if (ret < 0) + return ret; + + return 0; +} + +static int request_cmvs(struct uea_softc *sc, + struct uea_cmvs **cmvs, const struct firmware **fw) +{ + int ret, size; + u8 *data; + char *file; + static char cmv_name[256] = FW_DIR; + + if (cmv_file[sc->modem_index] == NULL) { + if (UEA_CHIP_VERSION(sc) == ADI930) + file = (IS_ISDN(sc)) ? "CMV9i.bin" : "CMV9p.bin"; + else + file = (IS_ISDN(sc)) ? "CMVei.bin" : "CMVep.bin"; + } else + file = cmv_file[sc->modem_index]; + + strcpy(cmv_name, FW_DIR); + strlcat(cmv_name, file, sizeof(cmv_name)); + + ret = request_firmware(fw, cmv_name, &sc->usb_dev->dev); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "requesting firmware %s failed with error %d\n", + cmv_name, ret); + return ret; + } + + data = (u8 *) (*fw)->data; + size = *data * sizeof(struct uea_cmvs) + 1; + if (size != (*fw)->size) { + uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", + cmv_name); + release_firmware(*fw); + return -EILSEQ; + } + + *cmvs = (struct uea_cmvs *)(data + 1); + return *data; +} + +/* Start boot post firmware modem: + * - send reset commands through usb control pipe + * - start workqueue for DSP loading + * - send CMV options to modem + */ + +static int uea_start_reset(struct uea_softc *sc) +{ + u16 zero = 0; /* ;-) */ + int i, len, ret; + struct uea_cmvs *cmvs; + const struct firmware *cmvs_fw; + + uea_enters(INS_TO_USBDEV(sc)); + uea_info(INS_TO_USBDEV(sc), "(re)booting started\n"); + + sc->booting = 1; + UPDATE_ATM_STAT(signal, ATM_PHY_SIG_LOST); + + /* reset statistics */ + memset(&sc->stats, 0, sizeof(struct uea_stats)); + + /* tell the modem that we want to boot in IDMA mode */ + uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL); + uea_request(sc, UEA_SET_MODE, UEA_BOOT_IDMA, 0, NULL); + + /* enter reset mode */ + uea_request(sc, UEA_SET_MODE, UEA_START_RESET, 0, NULL); + + /* original driver use 200ms, but windows driver use 100ms */ + msleep(100); + + /* leave reset mode */ + uea_request(sc, UEA_SET_MODE, UEA_END_RESET, 0, NULL); + + /* clear tx and rx mailboxes */ + uea_request(sc, UEA_SET_2183_DATA, UEA_MPTX_MAILBOX, 2, &zero); + uea_request(sc, UEA_SET_2183_DATA, UEA_MPRX_MAILBOX, 2, &zero); + uea_request(sc, UEA_SET_2183_DATA, UEA_SWAP_MAILBOX, 2, &zero); + + msleep(1000); + sc->cmv_function = MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY); + sc->booting = 0; + + /* start loading DSP */ + sc->pageno = 0; + sc->ovl = 0; + schedule_work(&sc->task); + + /* wait for modem ready CMV */ + ret = wait_cmv_ack(sc); + if (ret < 0) + return ret; + + /* Enter in R-IDLE (cmv) until instructed otherwise */ + ret = uea_write_cmv(sc, SA_CNTL, 0, 1); + if (ret < 0) + return ret; + + /* get options */ + ret = len = request_cmvs(sc, &cmvs, &cmvs_fw); + if (ret < 0) + return ret; + + /* send options */ + for (i = 0; i < len; i++) { + ret = uea_write_cmv(sc, FW_GET_LONG(&cmvs[i].address), + FW_GET_WORD(&cmvs[i].offset), + FW_GET_LONG(&cmvs[i].data)); + if (ret < 0) + goto out; + } + /* Enter in R-ACT-REQ */ + ret = uea_write_cmv(sc, SA_CNTL, 0, 2); +out: + release_firmware(cmvs_fw); + sc->reset = 0; + uea_leaves(INS_TO_USBDEV(sc)); + return ret; +} + +/* + * In case of an error wait 1s before rebooting the modem + * if the modem don't request reboot (-EAGAIN). + * Monitor the modem every 1s. + */ + +static int uea_kthread(void *data) +{ + struct uea_softc *sc = data; + int ret = -EAGAIN; + + uea_enters(INS_TO_USBDEV(sc)); + while (!kthread_should_stop()) { + if (ret < 0 || sc->reset) + ret = uea_start_reset(sc); + if (!ret) + ret = uea_stat(sc); + if (ret != -EAGAIN) + msleep(1000); + } + uea_leaves(INS_TO_USBDEV(sc)); + return ret; +} + +/* Load second usb firmware for ADI930 chip */ +static int load_XILINX_firmware(struct uea_softc *sc) +{ + const struct firmware *fw_entry; + int ret, size, u, ln; + u8 *pfw, value; + char *fw_name = FW_DIR "930-fpga.bin"; + + uea_enters(INS_TO_USBDEV(sc)); + + ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev); + if (ret) { + uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n", + fw_name); + goto err0; + } + + pfw = fw_entry->data; + size = fw_entry->size; + if (size != 0x577B) { + uea_err(INS_TO_USBDEV(sc), "firmware %s is corrupted\n", + fw_name); + ret = -EILSEQ; + goto err1; + } + for (u = 0; u < size; u += ln) { + ln = min(size - u, 64); + ret = uea_request(sc, 0xe, 0, ln, pfw + u); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "elsa download data failed (%d)\n", ret); + goto err1; + } + } + + /* finish to send the fpga + */ + ret = uea_request(sc, 0xe, 1, 0, NULL); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "elsa download data failed (%d)\n", ret); + goto err1; + } + + /* + * Tell the modem we finish : de-assert reset + */ + value = 0; + ret = uea_send_modem_cmd(sc->usb_dev, 0xe, 1, &value); + if (ret < 0) + uea_err(sc->usb_dev, "elsa de-assert failed with error %d\n", ret); + + +err1: + release_firmware(fw_entry); +err0: + uea_leaves(INS_TO_USBDEV(sc)); + return ret; +} + +static void uea_dispatch_cmv(struct uea_softc *sc, struct cmv* cmv) +{ + uea_enters(INS_TO_USBDEV(sc)); + if (le16_to_cpu(cmv->wPreamble) != PREAMBLE) + goto bad1; + + if (cmv->bDirection != MODEMTOHOST) + goto bad1; + + /* FIXME : ADI930 reply wrong preambule (func = 2, sub = 2) to + * the first MEMACESS cmv. Ignore it... + */ + if (cmv->bFunction != sc->cmv_function) { + if (UEA_CHIP_VERSION(sc) == ADI930 + && cmv->bFunction == MAKEFUNCTION(2, 2)) { + cmv->wIndex = cpu_to_le16(sc->cmv_idx); + put_unaligned(cpu_to_le32(sc->cmv_address), &cmv->dwSymbolicAddress); + cmv->wOffsetAddress = cpu_to_le16(sc->cmv_offset); + } + else + goto bad2; + } + + if (cmv->bFunction == MAKEFUNCTION(ADSLDIRECTIVE, MODEMREADY)) { + wake_up_cmv_ack(sc); + return; + } + + /* in case of MEMACCESS */ + if (le16_to_cpu(cmv->wIndex) != sc->cmv_idx || + le32_to_cpu(get_unaligned(&cmv->dwSymbolicAddress)) != + sc->cmv_address + || le16_to_cpu(cmv->wOffsetAddress) != sc->cmv_offset) + goto bad2; + + sc->data = le32_to_cpu(get_unaligned(&cmv->dwData)); + sc->data = sc->data << 16 | sc->data >> 16; + + wake_up_cmv_ack(sc); + return; + +bad2: + uea_err(INS_TO_USBDEV(sc), "unexpected cmv received," + "Function : %d, Subfunction : %d\n", + FUNCTION_TYPE(cmv->bFunction), + FUNCTION_SUBTYPE(cmv->bFunction)); + return; + +bad1: + uea_err(INS_TO_USBDEV(sc), "invalid cmv received, " + "wPreamble %d, bDirection %d\n", + le16_to_cpu(cmv->wPreamble), cmv->bDirection); +} + +/* + * interrupt handler + */ +static void uea_intr(struct urb *urb, struct pt_regs *regs) +{ + struct uea_softc *sc = (struct uea_softc *)urb->context; + struct intr_pkt *intr; + uea_enters(INS_TO_USBDEV(sc)); + + if (urb->status < 0) { + uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n", + urb->status); + return; + } + + intr = (struct intr_pkt *) urb->transfer_buffer; + + /* device-to-host interrupt */ + if (intr->bType != 0x08 || sc->booting) { + uea_err(INS_TO_USBDEV(sc), "wrong intr\n"); + // rebooting ? + // sc->reset = 1; + goto resubmit; + } + + switch (le16_to_cpu(intr->wInterrupt)) { + case INT_LOADSWAPPAGE: + sc->pageno = intr->bSwapPageNo; + sc->ovl = intr->bOvl >> 4 | intr->bOvl << 4; + schedule_work(&sc->task); + break; + + case INT_INCOMINGCMV: + uea_dispatch_cmv(sc, &intr->u.s2.cmv); + break; + + default: + uea_err(INS_TO_USBDEV(sc), "unknown intr %u\n", + le16_to_cpu(intr->wInterrupt)); + } + +resubmit: + usb_submit_urb(sc->urb_int, GFP_ATOMIC); +} + +/* + * Start the modem : init the data and start kernel thread + */ +static int uea_boot(struct uea_softc *sc) +{ + int ret; + struct intr_pkt *intr; + + uea_enters(INS_TO_USBDEV(sc)); + + INIT_WORK(&sc->task, uea_load_page, sc); + init_waitqueue_head(&sc->sync_q); + init_waitqueue_head(&sc->cmv_ack_wait); + + if (UEA_CHIP_VERSION(sc) == ADI930) + load_XILINX_firmware(sc); + + intr = kmalloc(INTR_PKT_SIZE, GFP_KERNEL); + if (!intr) { + uea_err(INS_TO_USBDEV(sc), + "cannot allocate interrupt package\n"); + uea_leaves(INS_TO_USBDEV(sc)); + return -ENOMEM; + } + + sc->urb_int = usb_alloc_urb(0, GFP_KERNEL); + if (!sc->urb_int) { + uea_err(INS_TO_USBDEV(sc), "cannot allocate interrupt URB\n"); + goto err; + } + + usb_fill_int_urb(sc->urb_int, sc->usb_dev, + usb_rcvintpipe(sc->usb_dev, UEA_INTR_PIPE), + intr, INTR_PKT_SIZE, uea_intr, sc, + sc->usb_dev->actconfig->interface[0]->altsetting[0]. + endpoint[0].desc.bInterval); + + ret = usb_submit_urb(sc->urb_int, GFP_KERNEL); + if (ret < 0) { + uea_err(INS_TO_USBDEV(sc), + "urb submition failed with error %d\n", ret); + goto err1; + } + + sc->kthread = kthread_run(uea_kthread, sc, "ueagle-atm"); + if (sc->kthread == ERR_PTR(-ENOMEM)) { + uea_err(INS_TO_USBDEV(sc), "failed to create thread\n"); + goto err2; + } + + uea_leaves(INS_TO_USBDEV(sc)); + return 0; + +err2: + usb_kill_urb(sc->urb_int); +err1: + kfree(intr); +err: + usb_free_urb(sc->urb_int); + uea_leaves(INS_TO_USBDEV(sc)); + return -ENOMEM; +} + +/* + * Stop the modem : kill kernel thread and free data + */ +static void uea_stop(struct uea_softc *sc) +{ + int ret; + uea_enters(INS_TO_USBDEV(sc)); + ret = kthread_stop(sc->kthread); + uea_info(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret); + + /* stop any pending boot process */ + flush_scheduled_work(); + + uea_request(sc, UEA_SET_MODE, UEA_LOOPBACK_ON, 0, NULL); + + usb_kill_urb(sc->urb_int); + kfree(sc->urb_int->transfer_buffer); + usb_free_urb(sc->urb_int); + + if (sc->dsp_firm) + release_firmware(sc->dsp_firm); + uea_leaves(INS_TO_USBDEV(sc)); +} + +/* syfs interface */ +static struct uea_softc *dev_to_uea(struct device *dev) +{ + struct usb_interface *intf; + struct usbatm_data *usbatm; + + intf = to_usb_interface(dev); + if (!intf) + return NULL; + + usbatm = usb_get_intfdata(intf); + if (!usbatm) + return NULL; + + return usbatm->driver_data; +} + +static ssize_t read_status(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = -ENODEV; + struct uea_softc *sc; + + down(&uea_semaphore); + sc = dev_to_uea(dev); + if (!sc) + goto out; + ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.state); +out: + up(&uea_semaphore); + return ret; +} + +static ssize_t reboot(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -ENODEV; + struct uea_softc *sc; + + down(&uea_semaphore); + sc = dev_to_uea(dev); + if (!sc) + goto out; + sc->reset = 1; + ret = count; +out: + up(&uea_semaphore); + return ret; +} + +static DEVICE_ATTR(stat_status, S_IWUGO | S_IRUGO, read_status, reboot); + +static ssize_t read_human_status(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = -ENODEV; + struct uea_softc *sc; + + down(&uea_semaphore); + sc = dev_to_uea(dev); + if (!sc) + goto out; + + switch (GET_STATUS(sc->stats.phy.state)) { + case 0: + ret = sprintf(buf, "Modem is booting\n"); + break; + case 1: + ret = sprintf(buf, "Modem is initializing\n"); + break; + case 2: + ret = sprintf(buf, "Modem is operational\n"); + break; + default: + ret = sprintf(buf, "Modem synchronization failed\n"); + break; + } +out: + up(&uea_semaphore); + return ret; +} + +static DEVICE_ATTR(stat_human_status, S_IWUGO | S_IRUGO, read_human_status, NULL); + +static ssize_t read_delin(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int ret = -ENODEV; + struct uea_softc *sc; + + down(&uea_semaphore); + sc = dev_to_uea(dev); + if (!sc) + goto out; + + if (sc->stats.phy.flags & 0x0C00) + ret = sprintf(buf, "ERROR\n"); + else if (sc->stats.phy.flags & 0x0030) + ret = sprintf(buf, "LOSS\n"); + else + ret = sprintf(buf, "GOOD\n"); +out: + up(&uea_semaphore); + return ret; +} + +static DEVICE_ATTR(stat_delin, S_IWUGO | S_IRUGO, read_delin, NULL); + +#define UEA_ATTR(name, reset) \ + \ +static ssize_t read_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int ret = -ENODEV; \ + struct uea_softc *sc; \ + \ + down(&uea_semaphore); \ + sc = dev_to_uea(dev); \ + if (!sc) \ + goto out; \ + ret = snprintf(buf, 10, "%08x\n", sc->stats.phy.name); \ + if (reset) \ + sc->stats.phy.name = 0; \ +out: \ + up(&uea_semaphore); \ + return ret; \ +} \ + \ +static DEVICE_ATTR(stat_##name, S_IRUGO, read_##name, NULL) + +UEA_ATTR(mflags, 1); +UEA_ATTR(vidcpe, 0); +UEA_ATTR(usrate, 0); +UEA_ATTR(dsrate, 0); +UEA_ATTR(usattenuation, 0); +UEA_ATTR(dsattenuation, 0); +UEA_ATTR(usmargin, 0); +UEA_ATTR(dsmargin, 0); +UEA_ATTR(txflow, 0); +UEA_ATTR(rxflow, 0); +UEA_ATTR(uscorr, 0); +UEA_ATTR(dscorr, 0); +UEA_ATTR(usunc, 0); +UEA_ATTR(dsunc, 0); + +/* Retrieve the device End System Identifier (MAC) */ + +#define htoi(x) (isdigit(x) ? x-'0' : toupper(x)-'A'+10) +static int uea_getesi(struct uea_softc *sc, u_char * esi) +{ + unsigned char mac_str[2 * ETH_ALEN + 1]; + int i; + if (usb_string + (sc->usb_dev, sc->usb_dev->descriptor.iSerialNumber, mac_str, + sizeof(mac_str)) != 2 * ETH_ALEN) + return 1; + + for (i = 0; i < ETH_ALEN; i++) + esi[i] = htoi(mac_str[2 * i]) * 16 + htoi(mac_str[2 * i + 1]); + + return 0; +} + +/* ATM stuff */ +static int uea_atm_open(struct usbatm_data *usbatm, struct atm_dev *atm_dev) +{ + struct uea_softc *sc = usbatm->driver_data; + + return uea_getesi(sc, atm_dev->esi); +} + +static int uea_heavy(struct usbatm_data *usbatm, struct usb_interface *intf) +{ + struct uea_softc *sc = usbatm->driver_data; + + wait_event(sc->sync_q, IS_OPERATIONAL(sc)); + + return 0; + +} + +static int claim_interface(struct usb_device *usb_dev, + struct usbatm_data *usbatm, int ifnum) +{ + int ret; + struct usb_interface *intf = usb_ifnum_to_if(usb_dev, ifnum); + + if (!intf) { + uea_err(usb_dev, "interface %d not found\n", ifnum); + return -ENODEV; + } + + ret = usb_driver_claim_interface(&uea_driver, intf, usbatm); + if (ret != 0) + uea_err(usb_dev, "can't claim interface %d, error %d\n", ifnum, + ret); + return ret; +} + +static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf) +{ + /* sysfs interface */ + device_create_file(&intf->dev, &dev_attr_stat_status); + device_create_file(&intf->dev, &dev_attr_stat_mflags); + device_create_file(&intf->dev, &dev_attr_stat_human_status); + device_create_file(&intf->dev, &dev_attr_stat_delin); + device_create_file(&intf->dev, &dev_attr_stat_vidcpe); + device_create_file(&intf->dev, &dev_attr_stat_usrate); + device_create_file(&intf->dev, &dev_attr_stat_dsrate); + device_create_file(&intf->dev, &dev_attr_stat_usattenuation); + device_create_file(&intf->dev, &dev_attr_stat_dsattenuation); + device_create_file(&intf->dev, &dev_attr_stat_usmargin); + device_create_file(&intf->dev, &dev_attr_stat_dsmargin); + device_create_file(&intf->dev, &dev_attr_stat_txflow); + device_create_file(&intf->dev, &dev_attr_stat_rxflow); + device_create_file(&intf->dev, &dev_attr_stat_uscorr); + device_create_file(&intf->dev, &dev_attr_stat_dscorr); + device_create_file(&intf->dev, &dev_attr_stat_usunc); + device_create_file(&intf->dev, &dev_attr_stat_dsunc); +} + +static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf, + const struct usb_device_id *id, int *heavy) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct uea_softc *sc; + int ret, ifnum = intf->altsetting->desc.bInterfaceNumber; + + uea_enters(usb); + + /* interface 0 is for firmware/monitoring */ + if (ifnum != UEA_INTR_IFACE_NO) + return -ENODEV; + + *heavy = sync_wait[modem_index]; + + /* interface 1 is for outbound traffic */ + ret = claim_interface(usb, usbatm, UEA_US_IFACE_NO); + if (ret < 0) + return ret; + + /* ADI930 has only 2 interfaces and inbound traffic + * is on interface 1 + */ + if (UEA_CHIP_VERSION(id) != ADI930) { + /* interface 2 is for inbound traffic */ + ret = claim_interface(usb, usbatm, UEA_DS_IFACE_NO); + if (ret < 0) + return ret; + } + + sc = kzalloc(sizeof(struct uea_softc), GFP_KERNEL); + if (!sc) { + uea_err(INS_TO_USBDEV(sc), "uea_init: not enough memory !\n"); + return -ENOMEM; + } + + sc->usb_dev = usb; + usbatm->driver_data = sc; + sc->usbatm = usbatm; + sc->modem_index = (modem_index < NB_MODEM) ? modem_index++ : 0; + sc->driver_info = id->driver_info; + + ret = uea_boot(sc); + if (ret < 0) { + kfree(sc); + return ret; + } + + create_fs_entries(sc, intf); + return 0; +} + +static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf) +{ + /* sysfs interface */ + device_remove_file(&intf->dev, &dev_attr_stat_status); + device_remove_file(&intf->dev, &dev_attr_stat_mflags); + device_remove_file(&intf->dev, &dev_attr_stat_human_status); + device_remove_file(&intf->dev, &dev_attr_stat_delin); + device_remove_file(&intf->dev, &dev_attr_stat_vidcpe); + device_remove_file(&intf->dev, &dev_attr_stat_usrate); + device_remove_file(&intf->dev, &dev_attr_stat_dsrate); + device_remove_file(&intf->dev, &dev_attr_stat_usattenuation); + device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation); + device_remove_file(&intf->dev, &dev_attr_stat_usmargin); + device_remove_file(&intf->dev, &dev_attr_stat_dsmargin); + device_remove_file(&intf->dev, &dev_attr_stat_txflow); + device_remove_file(&intf->dev, &dev_attr_stat_rxflow); + device_remove_file(&intf->dev, &dev_attr_stat_uscorr); + device_remove_file(&intf->dev, &dev_attr_stat_dscorr); + device_remove_file(&intf->dev, &dev_attr_stat_usunc); + device_remove_file(&intf->dev, &dev_attr_stat_dsunc); +} + +static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf) +{ + struct uea_softc *sc = usbatm->driver_data; + + destroy_fs_entries(sc, intf); + uea_stop(sc); + kfree(sc); +} + +static struct usbatm_driver uea_usbatm_driver = { + .driver_name = "ueagle-atm", + .owner = THIS_MODULE, + .bind = uea_bind, + .atm_start = uea_atm_open, + .unbind = uea_unbind, + .heavy_init = uea_heavy, + .in = UEA_BULK_DATA_PIPE, + .out = UEA_BULK_DATA_PIPE, +}; + +static int uea_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *usb = interface_to_usbdev(intf); + + uea_enters(usb); + uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) : %s\n", + le16_to_cpu(usb->descriptor.idVendor), + le16_to_cpu(usb->descriptor.idProduct), + chip_name[UEA_CHIP_VERSION(id)]); + + usb_reset_device(usb); + + if (UEA_IS_PREFIRM(id)) + return uea_load_firmware(usb, UEA_CHIP_VERSION(id)); + + return usbatm_usb_probe(intf, id, &uea_usbatm_driver); +} + +static void uea_disconnect(struct usb_interface *intf) +{ + struct usb_device *usb = interface_to_usbdev(intf); + int ifnum = intf->altsetting->desc.bInterfaceNumber; + uea_enters(usb); + + /* ADI930 has 2 interfaces and eagle 3 interfaces. + * Pre-firmware device has one interface + */ + if (usb->config->desc.bNumInterfaces != 1 && ifnum == 0) { + down(&uea_semaphore); + usbatm_usb_disconnect(intf); + up(&uea_semaphore); + uea_info(usb, "ADSL device removed\n"); + } + + uea_leaves(usb); +} + +/* + * List of supported VID/PID + */ +static const struct usb_device_id uea_ids[] = { + {USB_DEVICE(ELSA_VID, ELSA_PID_PREFIRM), .driver_info = ADI930 | PREFIRM}, + {USB_DEVICE(ELSA_VID, ELSA_PID_PSTFIRM), .driver_info = ADI930 | PSTFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_I_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_II_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PREFIRM), .driver_info = EAGLE_II | PREFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_IIC_PID_PSTFIRM), .driver_info = EAGLE_II | PSTFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PREFIRM), .driver_info = EAGLE_III | PREFIRM}, + {USB_DEVICE(EAGLE_VID, EAGLE_III_PID_PSTFIRM), .driver_info = EAGLE_III | PSTFIRM}, + {USB_DEVICE(USR_VID, MILLER_A_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, + {USB_DEVICE(USR_VID, MILLER_A_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM}, + {USB_DEVICE(USR_VID, MILLER_B_PID_PREFIRM), .driver_info = EAGLE_I | PREFIRM}, + {USB_DEVICE(USR_VID, MILLER_B_PID_PSTFIRM), .driver_info = EAGLE_I | PSTFIRM}, + {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM}, + {USB_DEVICE(USR_VID, HEINEKEN_A_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM}, + {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PREFIRM),.driver_info = EAGLE_I | PREFIRM}, + {USB_DEVICE(USR_VID, HEINEKEN_B_PID_PSTFIRM),.driver_info = EAGLE_I | PSTFIRM}, + {} +}; + +/* + * USB driver descriptor + */ +static struct usb_driver uea_driver = { + .name = "ueagle-atm", + .id_table = uea_ids, + .probe = uea_probe, + .disconnect = uea_disconnect, +}; + +MODULE_DEVICE_TABLE(usb, uea_ids); + +/** + * uea_init - Initialize the module. + * Register to USB subsystem + */ +static int __init uea_init(void) +{ + printk(KERN_INFO "[ueagle-atm] driver " EAGLEUSBVERSION " loaded\n"); + + usb_register(&uea_driver); + + return 0; +} + +module_init(uea_init); + +/** + * uea_exit - Destroy module + * Deregister with USB subsystem + */ +static void __exit uea_exit(void) +{ + /* + * This calls automatically the uea_disconnect method if necessary: + */ + usb_deregister(&uea_driver); + + printk(KERN_INFO "[ueagle-atm] driver unloaded\n"); +} + +module_exit(uea_exit); + +MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka"); +MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 2e6593e6c1b..9baa6296fc9 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -646,14 +646,14 @@ static void usbatm_destroy_instance(struct kref *kref) kfree(instance); } -void usbatm_get_instance(struct usbatm_data *instance) +static void usbatm_get_instance(struct usbatm_data *instance) { dbg("%s", __func__); kref_get(&instance->refcount); } -void usbatm_put_instance(struct usbatm_data *instance) +static void usbatm_put_instance(struct usbatm_data *instance) { dbg("%s", __func__); diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index 7fe7fb484d1..5c76e3aaaa5 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -140,7 +140,6 @@ static int xusbatm_usb_probe(struct usb_interface *intf, } static struct usb_driver xusbatm_usb_driver = { - .owner = THIS_MODULE, .name = xusbatm_driver_name, .probe = xusbatm_usb_probe, .disconnect = usbatm_usb_disconnect, diff --git a/drivers/usb/class/audio.c b/drivers/usb/class/audio.c index 50858273f8d..3ad9ee8b84a 100644 --- a/drivers/usb/class/audio.c +++ b/drivers/usb/class/audio.c @@ -2732,7 +2732,6 @@ static struct usb_device_id usb_audio_ids [] = { MODULE_DEVICE_TABLE (usb, usb_audio_ids); static struct usb_driver usb_audio_driver = { - .owner = THIS_MODULE, .name = "audio", .probe = usb_audio_probe, .disconnect = usb_audio_disconnect, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 1b475141297..248279e44c9 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -6,6 +6,7 @@ * Copyright (c) 1999 Johannes Erdfelt <johannes@erdfelt.com> * Copyright (c) 2000 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2004 Oliver Neukum <oliver@neukum.name> + * Copyright (c) 2005 David Kubicek <dave@awk.cz> * * USB Abstract Control Model driver for USB modems and ISDN adapters * @@ -29,6 +30,7 @@ * config we want, sysadmin changes bConfigurationValue in sysfs. * v0.23 - use softirq for rx processing, as needed by tty layer * v0.24 - change probe method to evaluate CDC union descriptor + * v0.25 - downstream tasks paralelized to maximize throughput */ /* @@ -63,14 +65,15 @@ #include <linux/usb_cdc.h> #include <asm/byteorder.h> #include <asm/unaligned.h> +#include <linux/list.h> #include "cdc-acm.h" /* * Version Information */ -#define DRIVER_VERSION "v0.23" -#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik" +#define DRIVER_VERSION "v0.25" +#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek" #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters" static struct usb_driver acm_driver; @@ -284,7 +287,9 @@ exit: /* data interface returns incoming bytes, or we got unthrottled */ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) { - struct acm *acm = urb->context; + struct acm_rb *buf; + struct acm_ru *rcv = urb->context; + struct acm *acm = rcv->instance; dbg("Entering acm_read_bulk with status %d\n", urb->status); if (!ACM_READY(acm)) @@ -293,49 +298,109 @@ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs) if (urb->status) dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status); - /* calling tty_flip_buffer_push() in_irq() isn't allowed */ - tasklet_schedule(&acm->bh); + buf = rcv->buffer; + buf->size = urb->actual_length; + + spin_lock(&acm->read_lock); + list_add_tail(&rcv->list, &acm->spare_read_urbs); + list_add_tail(&buf->list, &acm->filled_read_bufs); + spin_unlock(&acm->read_lock); + + tasklet_schedule(&acm->urb_task); } static void acm_rx_tasklet(unsigned long _acm) { struct acm *acm = (void *)_acm; - struct urb *urb = acm->readurb; + struct acm_rb *buf; struct tty_struct *tty = acm->tty; - unsigned char *data = urb->transfer_buffer; + struct acm_ru *rcv; + //unsigned long flags; int i = 0; dbg("Entering acm_rx_tasklet"); - if (urb->actual_length > 0 && !acm->throttle) { - for (i = 0; i < urb->actual_length && !acm->throttle; i++) { - /* if we insert more than TTY_FLIPBUF_SIZE characters, - * we drop them. */ - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { - tty_flip_buffer_push(tty); - } - tty_insert_flip_char(tty, data[i], 0); - } - dbg("Handed %d bytes to tty layer", i+1); - tty_flip_buffer_push(tty); + if (!ACM_READY(acm) || acm->throttle) + return; + +next_buffer: + spin_lock(&acm->read_lock); + if (list_empty(&acm->filled_read_bufs)) { + spin_unlock(&acm->read_lock); + goto urbs; } + buf = list_entry(acm->filled_read_bufs.next, + struct acm_rb, list); + list_del(&buf->list); + spin_unlock(&acm->read_lock); + + dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d\n", buf, buf->size); + + for (i = 0; i < buf->size && !acm->throttle; i++) { + /* if we insert more than TTY_FLIPBUF_SIZE characters, + we drop them. */ + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty_flip_buffer_push(tty); + } + tty_insert_flip_char(tty, buf->base[i], 0); + } + tty_flip_buffer_push(tty); spin_lock(&acm->throttle_lock); if (acm->throttle) { dbg("Throtteling noticed"); - memmove(data, data + i, urb->actual_length - i); - urb->actual_length -= i; - acm->resubmit_to_unthrottle = 1; + memmove(buf->base, buf->base + i, buf->size - i); + buf->size -= i; spin_unlock(&acm->throttle_lock); + spin_lock(&acm->read_lock); + list_add(&buf->list, &acm->filled_read_bufs); + spin_unlock(&acm->read_lock); return; } spin_unlock(&acm->throttle_lock); - urb->actual_length = 0; - urb->dev = acm->dev; - - i = usb_submit_urb(urb, GFP_ATOMIC); - if (i) - dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i); + spin_lock(&acm->read_lock); + list_add(&buf->list, &acm->spare_read_bufs); + spin_unlock(&acm->read_lock); + goto next_buffer; + +urbs: + while (!list_empty(&acm->spare_read_bufs)) { + spin_lock(&acm->read_lock); + if (list_empty(&acm->spare_read_urbs)) { + spin_unlock(&acm->read_lock); + return; + } + rcv = list_entry(acm->spare_read_urbs.next, + struct acm_ru, list); + list_del(&rcv->list); + spin_unlock(&acm->read_lock); + + buf = list_entry(acm->spare_read_bufs.next, + struct acm_rb, list); + list_del(&buf->list); + + rcv->buffer = buf; + + usb_fill_bulk_urb(rcv->urb, acm->dev, + acm->rx_endpoint, + buf->base, + acm->readsize, + acm_read_bulk, rcv); + rcv->urb->transfer_dma = buf->dma; + rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p\n", rcv->urb, rcv, buf); + + /* This shouldn't kill the driver as unsuccessful URBs are returned to the + free-urbs-pool and resubmited ASAP */ + if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) { + list_add(&buf->list, &acm->spare_read_bufs); + spin_lock(&acm->read_lock); + list_add(&rcv->list, &acm->spare_read_urbs); + spin_unlock(&acm->read_lock); + return; + } + } } /* data interface wrote those outgoing bytes */ @@ -369,6 +434,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) { struct acm *acm; int rv = -EINVAL; + int i; dbg("Entering acm_tty_open.\n"); down(&open_sem); @@ -382,7 +448,9 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) tty->driver_data = acm; acm->tty = tty; - + /* force low_latency on so that our tty_push actually forces the data through, + otherwise it is scheduled, and with high data rates data can get lost. */ + tty->low_latency = 1; if (acm->used++) { goto done; @@ -394,18 +462,20 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) goto bail_out; } - acm->readurb->dev = acm->dev; - if (usb_submit_urb(acm->readurb, GFP_KERNEL)) { - dbg("usb_submit_urb(read bulk) failed"); - goto bail_out_and_unlink; - } - if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) goto full_bailout; - /* force low_latency on so that our tty_push actually forces the data through, - otherwise it is scheduled, and with high data rates data can get lost. */ - tty->low_latency = 1; + INIT_LIST_HEAD(&acm->spare_read_urbs); + INIT_LIST_HEAD(&acm->spare_read_bufs); + INIT_LIST_HEAD(&acm->filled_read_bufs); + for (i = 0; i < ACM_NRU; i++) { + list_add(&(acm->ru[i].list), &acm->spare_read_urbs); + } + for (i = 0; i < ACM_NRB; i++) { + list_add(&(acm->rb[i].list), &acm->spare_read_bufs); + } + + tasklet_schedule(&acm->urb_task); done: err_out: @@ -413,8 +483,6 @@ err_out: return rv; full_bailout: - usb_kill_urb(acm->readurb); -bail_out_and_unlink: usb_kill_urb(acm->ctrlurb); bail_out: acm->used--; @@ -424,18 +492,22 @@ bail_out: static void acm_tty_unregister(struct acm *acm) { + int i; + tty_unregister_device(acm_tty_driver, acm->minor); usb_put_intf(acm->control); acm_table[acm->minor] = NULL; usb_free_urb(acm->ctrlurb); - usb_free_urb(acm->readurb); usb_free_urb(acm->writeurb); + for (i = 0; i < ACM_NRU; i++) + usb_free_urb(acm->ru[i].urb); kfree(acm); } static void acm_tty_close(struct tty_struct *tty, struct file *filp) { struct acm *acm = tty->driver_data; + int i; if (!acm || !acm->used) return; @@ -446,7 +518,8 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp) acm_set_control(acm, acm->ctrlout = 0); usb_kill_urb(acm->ctrlurb); usb_kill_urb(acm->writeurb); - usb_kill_urb(acm->readurb); + for (i = 0; i < ACM_NRU; i++) + usb_kill_urb(acm->ru[i].urb); } else acm_tty_unregister(acm); } @@ -528,10 +601,7 @@ static void acm_tty_unthrottle(struct tty_struct *tty) spin_lock_bh(&acm->throttle_lock); acm->throttle = 0; spin_unlock_bh(&acm->throttle_lock); - if (acm->resubmit_to_unthrottle) { - acm->resubmit_to_unthrottle = 0; - acm_read_bulk(acm->readurb, NULL); - } + tasklet_schedule(&acm->urb_task); } static void acm_tty_break_ctl(struct tty_struct *tty, int state) @@ -588,7 +658,7 @@ static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int return -ENOIOCTLCMD; } -static __u32 acm_tty_speed[] = { +static const __u32 acm_tty_speed[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 576000, @@ -596,7 +666,7 @@ static __u32 acm_tty_speed[] = { 2500000, 3000000, 3500000, 4000000 }; -static __u8 acm_tty_size[] = { +static const __u8 acm_tty_size[] = { 5, 6, 7, 8 }; @@ -694,6 +764,7 @@ static int acm_probe (struct usb_interface *intf, int call_interface_num = -1; int data_interface_num; unsigned long quirks; + int i; /* handle quirks deadly to normal probing*/ quirks = (unsigned long)id->driver_info; @@ -833,7 +904,7 @@ skip_normal_probe: } ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); - readsize = le16_to_cpu(epread->wMaxPacketSize); + readsize = le16_to_cpu(epread->wMaxPacketSize)*2; acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize); acm->control = control_interface; acm->data = data_interface; @@ -842,12 +913,14 @@ skip_normal_probe: acm->ctrl_caps = ac_management_function; acm->ctrlsize = ctrlsize; acm->readsize = readsize; - acm->bh.func = acm_rx_tasklet; - acm->bh.data = (unsigned long) acm; + acm->urb_task.func = acm_rx_tasklet; + acm->urb_task.data = (unsigned long) acm; INIT_WORK(&acm->work, acm_softint, acm); spin_lock_init(&acm->throttle_lock); spin_lock_init(&acm->write_lock); + spin_lock_init(&acm->read_lock); acm->write_ready = 1; + acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress); buf = usb_buffer_alloc(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma); if (!buf) { @@ -856,13 +929,6 @@ skip_normal_probe: } acm->ctrl_buffer = buf; - buf = usb_buffer_alloc(usb_dev, readsize, GFP_KERNEL, &acm->read_dma); - if (!buf) { - dev_dbg(&intf->dev, "out of memory (read buffer alloc)\n"); - goto alloc_fail3; - } - acm->read_buffer = buf; - if (acm_write_buffers_alloc(acm) < 0) { dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n"); goto alloc_fail4; @@ -873,10 +939,25 @@ skip_normal_probe: dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n"); goto alloc_fail5; } - acm->readurb = usb_alloc_urb(0, GFP_KERNEL); - if (!acm->readurb) { - dev_dbg(&intf->dev, "out of memory (readurb kmalloc)\n"); - goto alloc_fail6; + for (i = 0; i < ACM_NRU; i++) { + struct acm_ru *rcv = &(acm->ru[i]); + + if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) { + dev_dbg(&intf->dev, "out of memory (read urbs usb_alloc_urb)\n"); + goto alloc_fail7; + } + + rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + rcv->instance = acm; + } + for (i = 0; i < ACM_NRB; i++) { + struct acm_rb *buf = &(acm->rb[i]); + + // Using usb_buffer_alloc instead of kmalloc as Oliver suggested + if (!(buf->base = usb_buffer_alloc(acm->dev, readsize, GFP_KERNEL, &buf->dma))) { + dev_dbg(&intf->dev, "out of memory (read bufs usb_buffer_alloc)\n"); + goto alloc_fail7; + } } acm->writeurb = usb_alloc_urb(0, GFP_KERNEL); if (!acm->writeurb) { @@ -889,15 +970,9 @@ skip_normal_probe: acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; acm->ctrlurb->transfer_dma = acm->ctrl_dma; - usb_fill_bulk_urb(acm->readurb, usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress), - acm->read_buffer, readsize, acm_read_bulk, acm); - acm->readurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; - acm->readurb->transfer_dma = acm->read_dma; - usb_fill_bulk_urb(acm->writeurb, usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), NULL, acm->writesize, acm_write_bulk, acm); acm->writeurb->transfer_flags |= URB_NO_FSBR | URB_NO_TRANSFER_DMA_MAP; - /* acm->writeurb->transfer_dma = 0; */ dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor); @@ -917,14 +992,14 @@ skip_normal_probe: return 0; alloc_fail7: - usb_free_urb(acm->readurb); -alloc_fail6: + for (i = 0; i < ACM_NRB; i++) + usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); + for (i = 0; i < ACM_NRU; i++) + usb_free_urb(acm->ru[i].urb); usb_free_urb(acm->ctrlurb); alloc_fail5: acm_write_buffers_free(acm); alloc_fail4: - usb_buffer_free(usb_dev, readsize, acm->read_buffer, acm->read_dma); -alloc_fail3: usb_buffer_free(usb_dev, ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); alloc_fail2: kfree(acm); @@ -936,6 +1011,7 @@ static void acm_disconnect(struct usb_interface *intf) { struct acm *acm = usb_get_intfdata (intf); struct usb_device *usb_dev = interface_to_usbdev(intf); + int i; if (!acm || !acm->dev) { dbg("disconnect on nonexisting interface"); @@ -946,15 +1022,24 @@ static void acm_disconnect(struct usb_interface *intf) acm->dev = NULL; usb_set_intfdata (intf, NULL); + tasklet_disable(&acm->urb_task); + usb_kill_urb(acm->ctrlurb); - usb_kill_urb(acm->readurb); usb_kill_urb(acm->writeurb); + for (i = 0; i < ACM_NRU; i++) + usb_kill_urb(acm->ru[i].urb); + + INIT_LIST_HEAD(&acm->filled_read_bufs); + INIT_LIST_HEAD(&acm->spare_read_bufs); + + tasklet_enable(&acm->urb_task); flush_scheduled_work(); /* wait for acm_softint */ acm_write_buffers_free(acm); - usb_buffer_free(usb_dev, acm->readsize, acm->read_buffer, acm->read_dma); usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma); + for (i = 0; i < ACM_NRB; i++) + usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); usb_driver_release_interface(&acm_driver, acm->data); @@ -1003,7 +1088,6 @@ static struct usb_device_id acm_ids[] = { MODULE_DEVICE_TABLE (usb, acm_ids); static struct usb_driver acm_driver = { - .owner = THIS_MODULE, .name = "cdc_acm", .probe = acm_probe, .disconnect = acm_disconnect, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 963a5dfd209..fd2aaccdcba 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -59,6 +59,9 @@ * when processing onlcr, so we only need 2 buffers. */ #define ACM_NWB 2 +#define ACM_NRU 16 +#define ACM_NRB 16 + struct acm_wb { unsigned char *buf; dma_addr_t dmah; @@ -66,22 +69,43 @@ struct acm_wb { int use; }; +struct acm_rb { + struct list_head list; + int size; + unsigned char *base; + dma_addr_t dma; +}; + +struct acm_ru { + struct list_head list; + struct acm_rb *buffer; + struct urb *urb; + struct acm *instance; +}; + struct acm { struct usb_device *dev; /* the corresponding usb device */ struct usb_interface *control; /* control interface */ struct usb_interface *data; /* data interface */ struct tty_struct *tty; /* the corresponding tty */ - struct urb *ctrlurb, *readurb, *writeurb; /* urbs */ - u8 *ctrl_buffer, *read_buffer; /* buffers of urbs */ - dma_addr_t ctrl_dma, read_dma; /* dma handles of buffers */ + struct urb *ctrlurb, *writeurb; /* urbs */ + u8 *ctrl_buffer; /* buffers of urbs */ + dma_addr_t ctrl_dma; /* dma handles of buffers */ struct acm_wb wb[ACM_NWB]; + struct acm_ru ru[ACM_NRU]; + struct acm_rb rb[ACM_NRB]; + int rx_endpoint; + spinlock_t read_lock; + struct list_head spare_read_urbs; + struct list_head spare_read_bufs; + struct list_head filled_read_bufs; int write_current; /* current write buffer */ int write_used; /* number of non-empty write buffers */ int write_ready; /* write urb is not running */ spinlock_t write_lock; 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 */ + struct tasklet_struct urb_task; /* rx processing */ spinlock_t throttle_lock; /* synchronize throtteling and read callback */ unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ unsigned int ctrlout; /* output control lines (DTR, RTS) */ @@ -91,7 +115,6 @@ struct acm { unsigned int minor; /* acm minor number */ unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ - unsigned char resubmit_to_unthrottle; /* throtteling has disabled the read urb */ unsigned int ctrl_caps; /* control capabilities from the class specific header */ }; diff --git a/drivers/usb/class/usb-midi.c b/drivers/usb/class/usb-midi.c index 5f8af35e763..f13f004d311 100644 --- a/drivers/usb/class/usb-midi.c +++ b/drivers/usb/class/usb-midi.c @@ -2027,7 +2027,6 @@ static struct usb_device_id id_table[] = { }; static struct usb_driver usb_midi_driver = { - .owner = THIS_MODULE, .name = "midi", .probe = usb_midi_probe, .disconnect = usb_midi_disconnect, diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 357e75335f1..27e9404547f 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -199,7 +199,7 @@ struct quirk_printer_struct { #define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */ #define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */ -static struct quirk_printer_struct quirk_printers[] = { +static const struct quirk_printer_struct quirk_printers[] = { { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */ { 0x03f0, 0x0104, USBLP_QUIRK_BIDIR }, /* HP DeskJet 880C */ { 0x03f0, 0x0204, USBLP_QUIRK_BIDIR }, /* HP DeskJet 815C */ @@ -301,7 +301,7 @@ static void usblp_bulk_write(struct urb *urb, struct pt_regs *regs) * Get and print printer errors. */ -static char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" }; +static const char *usblp_messages[] = { "ok", "out of paper", "off-line", "on fire" }; static int usblp_check_status(struct usblp *usblp, int err) { @@ -438,7 +438,7 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM); } -static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct usblp *usblp = file->private_data; int length, err, i; @@ -838,7 +838,8 @@ static struct file_operations usblp_fops = { .read = usblp_read, .write = usblp_write, .poll = usblp_poll, - .ioctl = usblp_ioctl, + .unlocked_ioctl = usblp_ioctl, + .compat_ioctl = usblp_ioctl, .open = usblp_open, .release = usblp_release, }; @@ -849,6 +850,20 @@ static struct usb_class_driver usblp_class = { .minor_base = USBLP_MINOR_BASE, }; +static ssize_t usblp_show_ieee1284_id(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usblp *usblp = usb_get_intfdata (intf); + + if (usblp->device_id_string[0] == 0 && + usblp->device_id_string[1] == 0) + return 0; + + return sprintf(buf, "%s", usblp->device_id_string+2); +} + +static DEVICE_ATTR(ieee1284_id, S_IRUGO, usblp_show_ieee1284_id, NULL); + static int usblp_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -933,20 +948,12 @@ static int usblp_probe(struct usb_interface *intf, /* Retrieve and store the device ID string. */ usblp_cache_device_id_string(usblp); + device_create_file(&intf->dev, &dev_attr_ieee1284_id); #ifdef DEBUG usblp_check_status(usblp, 0); #endif - info("usblp%d: USB %sdirectional printer dev %d " - "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X", - usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum, - usblp->ifnum, - usblp->protocol[usblp->current_protocol].alt_setting, - usblp->current_protocol, - le16_to_cpu(usblp->dev->descriptor.idVendor), - le16_to_cpu(usblp->dev->descriptor.idProduct)); - usb_set_intfdata (intf, usblp); usblp->present = 1; @@ -957,11 +964,20 @@ static int usblp_probe(struct usb_interface *intf, goto abort_intfdata; } usblp->minor = intf->minor; + info("usblp%d: USB %sdirectional printer dev %d " + "if %d alt %d proto %d vid 0x%4.4X pid 0x%4.4X", + usblp->minor, usblp->bidir ? "Bi" : "Uni", dev->devnum, + usblp->ifnum, + usblp->protocol[usblp->current_protocol].alt_setting, + usblp->current_protocol, + le16_to_cpu(usblp->dev->descriptor.idVendor), + le16_to_cpu(usblp->dev->descriptor.idProduct)); return 0; abort_intfdata: usb_set_intfdata (intf, NULL); + device_remove_file(&intf->dev, &dev_attr_ieee1284_id); abort: if (usblp) { if (usblp->writebuf) @@ -1156,6 +1172,8 @@ static void usblp_disconnect(struct usb_interface *intf) BUG (); } + device_remove_file(&intf->dev, &dev_attr_ieee1284_id); + down (&usblp_sem); down (&usblp->sem); usblp->present = 0; @@ -1186,7 +1204,6 @@ static struct usb_device_id usblp_ids [] = { MODULE_DEVICE_TABLE (usb, usblp_ids); static struct usb_driver usblp_driver = { - .owner = THIS_MODULE, .name = "usblp", .probe = usblp_probe, .disconnect = usblp_disconnect, diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 86d5c380892..28329ddf187 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -2,7 +2,7 @@ # Makefile for USB Core files and filesystem # -usbcore-objs := usb.o hub.o hcd.o urb.o message.o \ +usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ config.o file.o buffer.o sysfs.o devio.o notify.o ifeq ($(CONFIG_PCI),y) diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 419c9943a7c..ad742cec94f 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -55,6 +55,9 @@ int hcd_buffer_create (struct usb_hcd *hcd) char name [16]; int i, size; + if (!hcd->self.controller->dma_mask) + return 0; + for (i = 0; i < HCD_BUFFER_POOLS; i++) { if (!(size = pool_max [i])) continue; diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83e815d3cd5..2684e15b813 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -67,45 +67,45 @@ /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ #define ALLOW_SERIAL_NUMBER -static char *format_topo = +static const char *format_topo = /* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ "\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; -static char *format_string_manufacturer = +static const char *format_string_manufacturer = /* S: Manufacturer=xxxx */ "S: Manufacturer=%.100s\n"; -static char *format_string_product = +static const char *format_string_product = /* S: Product=xxxx */ "S: Product=%.100s\n"; #ifdef ALLOW_SERIAL_NUMBER -static char *format_string_serialnumber = +static const char *format_string_serialnumber = /* S: SerialNumber=xxxx */ "S: SerialNumber=%.100s\n"; #endif -static char *format_bandwidth = +static const char *format_bandwidth = /* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; -static char *format_device1 = +static const char *format_device1 = /* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; -static char *format_device2 = +static const char *format_device2 = /* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; -static char *format_config = +static const char *format_config = /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; -static char *format_iface = +static const char *format_iface = /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; -static char *format_endpt = +static const char *format_endpt = /* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */ "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n"; @@ -545,10 +545,10 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes, loff_t *ski struct usb_device *childdev = usbdev->children[chix]; if (childdev) { - down(&childdev->serialize); + usb_lock_device(childdev); ret = usb_device_dump(buffer, nbytes, skip_bytes, file_offset, childdev, bus, level + 1, chix, ++cnt); - up(&childdev->serialize); + usb_unlock_device(childdev); if (ret == -EFAULT) return total_written; total_written += ret; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index b1d6e9af732..2b68998fe4b 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -402,7 +402,6 @@ static void driver_disconnect(struct usb_interface *intf) } struct usb_driver usbfs_driver = { - .owner = THIS_MODULE, .name = "usbfs", .probe = driver_probe, .disconnect = driver_disconnect, @@ -1350,9 +1349,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* let kernel drivers try to (re)bind to the interface */ case USBDEVFS_CONNECT: usb_unlock_device(ps->dev); - usb_lock_all_devices(); bus_rescan_devices(intf->dev.bus); - usb_unlock_all_devices(); usb_lock_device(ps->dev); break; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c new file mode 100644 index 00000000000..076462c8ba2 --- /dev/null +++ b/drivers/usb/core/driver.c @@ -0,0 +1,472 @@ +/* + * drivers/usb/driver.c - most of the driver model stuff for usb + * + * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de> + * + * based on drivers/usb/usb.c which had the following copyrights: + * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999-2001 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 + * (C) Copyright David Brownell 2000-2004 + * (C) Copyright Yggdrasil Computing, Inc. 2000 + * (usb_device_id matching changes by Adam J. Richter) + * (C) Copyright Greg Kroah-Hartman 2002-2003 + * + * NOTE! This is not actually a driver at all, rather this is + * just a collection of helper routines that implement the + * generic USB things that the real drivers can use.. + * + */ + +#include <linux/config.h> +#include <linux/device.h> +#include <linux/usb.h> +#include "hcd.h" +#include "usb.h" + +static int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id); + +struct usb_dynid { + struct list_head node; + struct usb_device_id id; +}; + + +static int generic_probe(struct device *dev) +{ + return 0; +} +static int generic_remove(struct device *dev) +{ + struct usb_device *udev = to_usb_device(dev); + + /* if this is only an unbind, not a physical disconnect, then + * unconfigure the device */ + if (udev->state == USB_STATE_CONFIGURED) + usb_set_configuration(udev, 0); + + /* in case the call failed or the device was suspended */ + if (udev->state >= USB_STATE_CONFIGURED) + usb_disable_device(udev, 0); + return 0; +} + +struct device_driver usb_generic_driver = { + .owner = THIS_MODULE, + .name = "usb", + .bus = &usb_bus_type, + .probe = generic_probe, + .remove = generic_remove, +}; + +/* Fun hack to determine if the struct device is a + * usb device or a usb interface. */ +int usb_generic_driver_data; + +#ifdef CONFIG_HOTPLUG + +/* + * Adds a new dynamic USBdevice ID to this driver, + * and cause the driver to probe for all devices again. + */ +static ssize_t store_new_id(struct device_driver *driver, + const char *buf, size_t count) +{ + struct usb_driver *usb_drv = to_usb_driver(driver); + struct usb_dynid *dynid; + u32 idVendor = 0; + u32 idProduct = 0; + int fields = 0; + + fields = sscanf(buf, "%x %x", &idVendor, &idProduct); + if (fields < 2) + return -EINVAL; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + INIT_LIST_HEAD(&dynid->node); + dynid->id.idVendor = idVendor; + dynid->id.idProduct = idProduct; + dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE; + + spin_lock(&usb_drv->dynids.lock); + list_add_tail(&usb_drv->dynids.list, &dynid->node); + spin_unlock(&usb_drv->dynids.lock); + + if (get_driver(driver)) { + driver_attach(driver); + put_driver(driver); + } + + return count; +} +static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); + +static int usb_create_newid_file(struct usb_driver *usb_drv) +{ + int error = 0; + + if (usb_drv->no_dynamic_id) + goto exit; + + if (usb_drv->probe != NULL) + error = sysfs_create_file(&usb_drv->driver.kobj, + &driver_attr_new_id.attr); +exit: + return error; +} + +static void usb_remove_newid_file(struct usb_driver *usb_drv) +{ + if (usb_drv->no_dynamic_id) + return; + + if (usb_drv->probe != NULL) + sysfs_remove_file(&usb_drv->driver.kobj, + &driver_attr_new_id.attr); +} + +static void usb_free_dynids(struct usb_driver *usb_drv) +{ + struct usb_dynid *dynid, *n; + + spin_lock(&usb_drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &usb_drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&usb_drv->dynids.lock); +} +#else +static inline int usb_create_newid_file(struct usb_driver *usb_drv) +{ + return 0; +} + +static void usb_remove_newid_file(struct usb_driver *usb_drv) +{ +} + +static inline void usb_free_dynids(struct usb_driver *usb_drv) +{ +} +#endif + +static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *intf, + struct usb_driver *drv) +{ + struct usb_dynid *dynid; + + spin_lock(&drv->dynids.lock); + list_for_each_entry(dynid, &drv->dynids.list, node) { + if (usb_match_one_id(intf, &dynid->id)) { + spin_unlock(&drv->dynids.lock); + return &dynid->id; + } + } + spin_unlock(&drv->dynids.lock); + return NULL; +} + + +/* called from driver core with usb_bus_type.subsys writelock */ +static int usb_probe_interface(struct device *dev) +{ + struct usb_interface * intf = to_usb_interface(dev); + struct usb_driver * driver = to_usb_driver(dev->driver); + const struct usb_device_id *id; + int error = -ENODEV; + + dev_dbg(dev, "%s\n", __FUNCTION__); + + if (!driver->probe) + return error; + /* FIXME we'd much prefer to just resume it ... */ + if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) + return -EHOSTUNREACH; + + id = usb_match_id(intf, driver->id_table); + if (!id) + id = usb_match_dynamic_id(intf, driver); + if (id) { + dev_dbg(dev, "%s - got id\n", __FUNCTION__); + + /* Interface "power state" doesn't correspond to any hardware + * state whatsoever. We use it to record when it's bound to + * a driver that may start I/0: it's not frozen/quiesced. + */ + mark_active(intf); + intf->condition = USB_INTERFACE_BINDING; + error = driver->probe(intf, id); + if (error) { + mark_quiesced(intf); + intf->condition = USB_INTERFACE_UNBOUND; + } else + intf->condition = USB_INTERFACE_BOUND; + } + + return error; +} + +/* called from driver core with usb_bus_type.subsys writelock */ +static int usb_unbind_interface(struct device *dev) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usb_driver *driver = to_usb_driver(intf->dev.driver); + + intf->condition = USB_INTERFACE_UNBINDING; + + /* release all urbs for this interface */ + usb_disable_interface(interface_to_usbdev(intf), intf); + + if (driver && driver->disconnect) + driver->disconnect(intf); + + /* reset other interface state */ + usb_set_interface(interface_to_usbdev(intf), + intf->altsetting[0].desc.bInterfaceNumber, + 0); + usb_set_intfdata(intf, NULL); + intf->condition = USB_INTERFACE_UNBOUND; + mark_quiesced(intf); + + return 0; +} + +/* returns 0 if no match, 1 if match */ +static int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *intf; + struct usb_device *dev; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return 0; + + intf = interface->cur_altsetting; + dev = interface_to_usbdev(interface); + + if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && + id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && + id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) + return 0; + + /* No need to test id->bcdDevice_lo != 0, since 0 is never + greater than any unsigned number. */ + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && + (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && + (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && + (id->bDeviceClass != dev->descriptor.bDeviceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && + (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && + (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (id->bInterfaceClass != intf->desc.bInterfaceClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && + (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) + return 0; + + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && + (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) + return 0; + + return 1; +} +/** + * usb_match_id - find first usb_device_id matching device or interface + * @interface: the interface of interest + * @id: array of usb_device_id structures, terminated by zero entry + * + * usb_match_id searches an array of usb_device_id's and returns + * the first one matching the device or interface, or null. + * This is used when binding (or rebinding) a driver to an interface. + * Most USB device drivers will use this indirectly, through the usb core, + * but some layered driver frameworks use it directly. + * These device tables are exported with MODULE_DEVICE_TABLE, through + * modutils, to support the driver loading functionality of USB hotplugging. + * + * What Matches: + * + * The "match_flags" element in a usb_device_id controls which + * members are used. If the corresponding bit is set, the + * value in the device_id must match its corresponding member + * in the device or interface descriptor, or else the device_id + * does not match. + * + * "driver_info" is normally used only by device drivers, + * but you can create a wildcard "matches anything" usb_device_id + * as a driver's "modules.usbmap" entry if you provide an id with + * only a nonzero "driver_info" field. If you do this, the USB device + * driver's probe() routine should use additional intelligence to + * decide whether to bind to the specified interface. + * + * What Makes Good usb_device_id Tables: + * + * The match algorithm is very simple, so that intelligence in + * driver selection must come from smart driver id records. + * Unless you have good reasons to use another selection policy, + * provide match elements only in related groups, and order match + * specifiers from specific to general. Use the macros provided + * for that purpose if you can. + * + * The most specific match specifiers use device descriptor + * data. These are commonly used with product-specific matches; + * the USB_DEVICE macro lets you provide vendor and product IDs, + * and you can also match against ranges of product revisions. + * These are widely used for devices with application or vendor + * specific bDeviceClass values. + * + * Matches based on device class/subclass/protocol specifications + * are slightly more general; use the USB_DEVICE_INFO macro, or + * its siblings. These are used with single-function devices + * where bDeviceClass doesn't specify that each interface has + * its own class. + * + * Matches based on interface class/subclass/protocol are the + * most general; they let drivers bind to any interface on a + * multiple-function device. Use the USB_INTERFACE_INFO + * macro, or its siblings, to match class-per-interface style + * devices (as recorded in bDeviceClass). + * + * Within those groups, remember that not all combinations are + * meaningful. For example, don't give a product version range + * without vendor and product IDs; or specify a protocol without + * its associated class and subclass. + */ +const struct usb_device_id *usb_match_id(struct usb_interface *interface, + const struct usb_device_id *id) +{ + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return NULL; + + /* It is important to check that id->driver_info is nonzero, + since an entry that is all zeroes except for a nonzero + id->driver_info is the way to create an entry that + indicates that the driver want to examine every + device and interface. */ + for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || + id->driver_info; id++) { + if (usb_match_one_id(interface, id)) + return id; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(usb_match_id); + +int usb_device_match(struct device *dev, struct device_driver *drv) +{ + struct usb_interface *intf; + struct usb_driver *usb_drv; + const struct usb_device_id *id; + + /* check for generic driver, which we don't match any device with */ + if (drv == &usb_generic_driver) + return 0; + + intf = to_usb_interface(dev); + usb_drv = to_usb_driver(drv); + + id = usb_match_id(intf, usb_drv->id_table); + if (id) + return 1; + + id = usb_match_dynamic_id(intf, usb_drv); + if (id) + return 1; + return 0; +} + +/** + * usb_register_driver - register a USB driver + * @new_driver: USB operations for the driver + * @owner: module owner of this driver. + * + * Registers a USB driver with the USB core. The list of unattached + * interfaces will be rescanned whenever a new driver is added, allowing + * the new driver to attach to any recognized devices. + * Returns a negative error code on failure and 0 on success. + * + * NOTE: if you want your driver to use the USB major number, you must call + * usb_register_dev() to enable that functionality. This function no longer + * takes care of that. + */ +int usb_register_driver(struct usb_driver *new_driver, struct module *owner) +{ + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + new_driver->driver.name = (char *)new_driver->name; + new_driver->driver.bus = &usb_bus_type; + new_driver->driver.probe = usb_probe_interface; + new_driver->driver.remove = usb_unbind_interface; + new_driver->driver.owner = owner; + spin_lock_init(&new_driver->dynids.lock); + INIT_LIST_HEAD(&new_driver->dynids.list); + + retval = driver_register(&new_driver->driver); + + if (!retval) { + pr_info("%s: registered new driver %s\n", + usbcore_name, new_driver->name); + usbfs_update_special(); + usb_create_newid_file(new_driver); + } else { + printk(KERN_ERR "%s: error %d registering driver %s\n", + usbcore_name, retval, new_driver->name); + } + + return retval; +} +EXPORT_SYMBOL_GPL(usb_register_driver); + +/** + * usb_deregister - unregister a USB driver + * @driver: USB operations of the driver to unregister + * Context: must be able to sleep + * + * Unlinks the specified driver from the internal USB driver list. + * + * NOTE: If you called usb_register_dev(), you still need to call + * usb_deregister_dev() to clean up your driver's allocated minor numbers, + * this * call will no longer do it for you. + */ +void usb_deregister(struct usb_driver *driver) +{ + pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); + + usb_remove_newid_file(driver); + usb_free_dynids(driver); + driver_unregister(&driver->driver); + + usbfs_update_special(); +} +EXPORT_SYMBOL_GPL(usb_deregister); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index da24c31ee00..0018bbc4de3 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -857,9 +857,7 @@ static int register_root_hub (struct usb_device *usb_dev, return (retval < 0) ? retval : -EMSGSIZE; } - usb_lock_device (usb_dev); retval = usb_new_device (usb_dev); - usb_unlock_device (usb_dev); if (retval) { usb_dev->bus->root_hub = NULL; dev_err (parent_dev, "can't register root hub for %s, %d\n", @@ -1827,8 +1825,6 @@ int usb_add_hcd(struct usb_hcd *hcd, retval = -ENOMEM; goto err_allocate_root_hub; } - rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : - USB_SPEED_FULL; /* Although in principle hcd->driver->start() might need to use rhdev, * none of the current drivers do. @@ -1846,6 +1842,9 @@ int usb_add_hcd(struct usb_hcd *hcd, dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); hcd->remote_wakeup = hcd->can_wakeup; + rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH : + USB_SPEED_FULL; + rhdev->bus_mA = min(500u, hcd->power_budget); if ((retval = register_root_hub(rhdev, hcd)) != 0) goto err_register_root_hub; @@ -1891,7 +1890,10 @@ void usb_remove_hcd(struct usb_hcd *hcd) spin_lock_irq (&hcd_root_hub_lock); hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); + + down(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); + up(&usb_bus_list_lock); hcd->poll_rh = 0; del_timer_sync(&hcd->rh_timer); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index c8a1b350e2c..591b5aad1a1 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -380,6 +380,7 @@ extern int usb_find_interface_driver (struct usb_device *dev, #ifdef CONFIG_PM extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd); extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); +extern void usb_root_hub_lost_power (struct usb_device *rhdev); extern int hcd_bus_suspend (struct usb_bus *bus); extern int hcd_bus_resume (struct usb_bus *bus); #else diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f78bd124d29..650d5ee5871 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -32,7 +32,7 @@ #include "hub.h" /* Protect struct usb_device->state and ->children members - * Note: Both are also protected by ->serialize, except that ->state can + * Note: Both are also protected by ->dev.sem, except that ->state can * change to USB_STATE_NOTATTACHED even when the semaphore isn't held. */ static DEFINE_SPINLOCK(device_state_lock); @@ -515,6 +515,31 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) return ret; } + +/* caller has locked the hub device */ +static void hub_pre_reset(struct usb_hub *hub, int disable_ports) +{ + struct usb_device *hdev = hub->hdev; + int port1; + + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + if (hdev->children[port1 - 1]) { + usb_disconnect(&hdev->children[port1 - 1]); + if (disable_ports) + hub_port_disable(hub, port1, 0); + } + } + hub_quiesce(hub); +} + +/* caller has locked the hub device */ +static void hub_post_reset(struct usb_hub *hub) +{ + hub_activate(hub); + hub_power_on(hub); +} + + static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { @@ -677,26 +702,40 @@ static int hub_configure(struct usb_hub *hub, * and battery-powered root hubs (may provide just 8 mA). */ ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); - if (ret < 0) { + if (ret < 2) { message = "can't get hub status"; goto fail; } le16_to_cpus(&hubstatus); if (hdev == hdev->bus->root_hub) { - struct usb_hcd *hcd = - container_of(hdev->bus, struct usb_hcd, self); - - hub->power_budget = min(500u, hcd->power_budget) / 2; + if (hdev->bus_mA == 0 || hdev->bus_mA >= 500) + hub->mA_per_port = 500; + else { + hub->mA_per_port = hdev->bus_mA; + hub->limited_power = 1; + } } else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_dbg(hub_dev, "hub controller current requirement: %dmA\n", hub->descriptor->bHubContrCurrent); - hub->power_budget = (501 - hub->descriptor->bHubContrCurrent) - / 2; + hub->limited_power = 1; + if (hdev->maxchild > 0) { + int remaining = hdev->bus_mA - + hub->descriptor->bHubContrCurrent; + + if (remaining < hdev->maxchild * 100) + dev_warn(hub_dev, + "insufficient power available " + "to use all downstream ports\n"); + hub->mA_per_port = 100; /* 7.2.1.1 */ + } + } else { /* Self-powered external hub */ + /* FIXME: What about battery-powered external hubs that + * provide less current per port? */ + hub->mA_per_port = 500; } - if (hub->power_budget) - dev_dbg(hub_dev, "%dmA bus power budget for children\n", - hub->power_budget * 2); - + if (hub->mA_per_port < 500) + dev_dbg(hub_dev, "%umA bus power budget for each child\n", + hub->mA_per_port); ret = hub_hub_status(hub, &hubstatus, &hubchange); if (ret < 0) { @@ -750,29 +789,10 @@ fail: static unsigned highspeed_hubs; -/* Called after the hub driver is unbound from a hub with children */ -static void hub_remove_children_work(void *__hub) -{ - struct usb_hub *hub = __hub; - struct usb_device *hdev = hub->hdev; - int i; - - kfree(hub); - - usb_lock_device(hdev); - for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); - } - usb_unlock_device(hdev); - usb_put_dev(hdev); -} - static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev; - int n, port1; usb_set_intfdata (intf, NULL); hdev = hub->hdev; @@ -780,7 +800,9 @@ static void hub_disconnect(struct usb_interface *intf) if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; - hub_quiesce(hub); + /* Disconnect all children and quiesce the hub */ + hub_pre_reset(hub, 1); + usb_free_urb(hub->urb); hub->urb = NULL; @@ -800,27 +822,7 @@ static void hub_disconnect(struct usb_interface *intf) hub->buffer = NULL; } - /* If there are any children then this is an unbind only, not a - * physical disconnection. The active ports must be disabled - * and later on we must call usb_disconnect(). We can't call - * it now because we may not hold the hub's device lock. - */ - n = 0; - for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - if (hdev->children[port1 - 1]) { - ++n; - hub_port_disable(hub, port1, 1); - } - } - - if (n == 0) - kfree(hub); - else { - /* Reuse the hub->leds work_struct for our own purposes */ - INIT_WORK(&hub->leds, hub_remove_children_work, hub); - schedule_work(&hub->leds); - usb_get_dev(hdev); - } + kfree(hub); } static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -917,26 +919,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } } -/* caller has locked the hub device */ -static void hub_pre_reset(struct usb_hub *hub) -{ - struct usb_device *hdev = hub->hdev; - int i; - - for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); - } - hub_quiesce(hub); -} - -/* caller has locked the hub device */ -static void hub_post_reset(struct usb_hub *hub) -{ - hub_activate(hub); - hub_power_on(hub); -} - /* grab device/port lock, returning index of that port (zero based). * protects the upstream link used by this device from concurrent @@ -964,24 +946,21 @@ static int locktree(struct usb_device *udev) t = locktree(hdev); if (t < 0) return t; - for (t = 0; t < hdev->maxchild; t++) { - if (hdev->children[t] == udev) { - /* everything is fail-fast once disconnect - * processing starts - */ - if (udev->state == USB_STATE_NOTATTACHED) - break; - /* when everyone grabs locks top->bottom, - * non-overlapping work may be concurrent - */ - down(&udev->serialize); - up(&hdev->serialize); - return t + 1; - } + /* everything is fail-fast once disconnect + * processing starts + */ + if (udev->state == USB_STATE_NOTATTACHED) { + usb_unlock_device(hdev); + return -ENODEV; } + + /* when everyone grabs locks top->bottom, + * non-overlapping work may be concurrent + */ + usb_lock_device(udev); usb_unlock_device(hdev); - return -ENODEV; + return udev->portnum; } static void recursively_mark_NOTATTACHED(struct usb_device *udev) @@ -1039,6 +1018,39 @@ void usb_set_device_state(struct usb_device *udev, EXPORT_SYMBOL(usb_set_device_state); +#ifdef CONFIG_PM + +/** + * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power + * @rhdev: struct usb_device for the root hub + * + * The USB host controller driver calls this function when its root hub + * is resumed and Vbus power has been interrupted or the controller + * has been reset. The routine marks all the children of the root hub + * as NOTATTACHED and marks logical connect-change events on their ports. + */ +void usb_root_hub_lost_power(struct usb_device *rhdev) +{ + struct usb_hub *hub; + int port1; + unsigned long flags; + + dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); + spin_lock_irqsave(&device_state_lock, flags); + hub = hdev_to_hub(rhdev); + for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { + if (rhdev->children[port1 - 1]) { + recursively_mark_NOTATTACHED( + rhdev->children[port1 - 1]); + set_bit(port1, hub->change_bits); + } + } + spin_unlock_irqrestore(&device_state_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); + +#endif + static void choose_address(struct usb_device *udev) { int devnum; @@ -1099,16 +1111,10 @@ void usb_disconnect(struct usb_device **pdev) * this quiesces everyting except pending urbs. */ usb_set_device_state(udev, USB_STATE_NOTATTACHED); - - /* lock the bus list on behalf of HCDs unregistering their root hubs */ - if (!udev->parent) { - down(&usb_bus_list_lock); - usb_lock_device(udev); - } else - down(&udev->serialize); - dev_info (&udev->dev, "USB disconnect, address %d\n", udev->devnum); + usb_lock_device(udev); + /* Free up all the children before we remove this device */ for (i = 0; i < USB_MAXCHILDREN; i++) { if (udev->children[i]) @@ -1136,54 +1142,112 @@ void usb_disconnect(struct usb_device **pdev) *pdev = NULL; spin_unlock_irq(&device_state_lock); - if (!udev->parent) { - usb_unlock_device(udev); - up(&usb_bus_list_lock); - } else - up(&udev->serialize); + usb_unlock_device(udev); device_unregister(&udev->dev); } +static inline const char *plural(int n) +{ + return (n == 1 ? "" : "s"); +} + static int choose_configuration(struct usb_device *udev) { - int c, i; + int i; + u16 devstatus; + int bus_powered; + int num_configs; + struct usb_host_config *c, *best; + + /* If this fails, assume the device is bus-powered */ + devstatus = 0; + usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); + le16_to_cpus(&devstatus); + bus_powered = ((devstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0); + dev_dbg(&udev->dev, "device is %s-powered\n", + bus_powered ? "bus" : "self"); + + best = NULL; + c = udev->config; + num_configs = udev->descriptor.bNumConfigurations; + for (i = 0; i < num_configs; (i++, c++)) { + struct usb_interface_descriptor *desc = + &c->intf_cache[0]->altsetting->desc; + + /* + * HP's USB bus-powered keyboard has only one configuration + * and it claims to be self-powered; other devices may have + * similar errors in their descriptors. If the next test + * were allowed to execute, such configurations would always + * be rejected and the devices would not work as expected. + */ +#if 0 + /* Rule out self-powered configs for a bus-powered device */ + if (bus_powered && (c->desc.bmAttributes & + USB_CONFIG_ATT_SELFPOWER)) + continue; +#endif - /* NOTE: this should interact with hub power budgeting */ + /* + * The next test may not be as effective as it should be. + * Some hubs have errors in their descriptor, claiming + * to be self-powered when they are really bus-powered. + * We will overestimate the amount of current such hubs + * make available for each port. + * + * This is a fairly benign sort of failure. It won't + * cause us to reject configurations that we should have + * accepted. + */ - c = udev->config[0].desc.bConfigurationValue; - if (udev->descriptor.bNumConfigurations != 1) { - for (i = 0; i < udev->descriptor.bNumConfigurations; i++) { - struct usb_interface_descriptor *desc; + /* Rule out configs that draw too much bus current */ + if (c->desc.bMaxPower * 2 > udev->bus_mA) + continue; - /* heuristic: Linux is more likely to have class - * drivers, so avoid vendor-specific interfaces. - */ - desc = &udev->config[i].intf_cache[0] - ->altsetting->desc; - if (desc->bInterfaceClass == USB_CLASS_VENDOR_SPEC) - continue; - /* COMM/2/all is CDC ACM, except 0xff is MSFT RNDIS. - * MSFT needs this to be the first config; never use - * it as the default unless Linux has host-side RNDIS. - * A second config would ideally be CDC-Ethernet, but - * may instead be the "vendor specific" CDC subset - * long used by ARM Linux for sa1100 or pxa255. - */ - if (desc->bInterfaceClass == USB_CLASS_COMM - && desc->bInterfaceSubClass == 2 - && desc->bInterfaceProtocol == 0xff) { - c = udev->config[1].desc.bConfigurationValue; - continue; - } - c = udev->config[i].desc.bConfigurationValue; + /* If the first config's first interface is COMM/2/0xff + * (MSFT RNDIS), rule it out unless Linux has host-side + * RNDIS support. */ + if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM + && desc->bInterfaceSubClass == 2 + && desc->bInterfaceProtocol == 0xff) { +#ifndef CONFIG_USB_NET_RNDIS + continue; +#else + best = c; +#endif + } + + /* From the remaining configs, choose the first one whose + * first interface is for a non-vendor-specific class. + * Reason: Linux is more likely to have a class driver + * than a vendor-specific driver. */ + else if (udev->descriptor.bDeviceClass != + USB_CLASS_VENDOR_SPEC && + desc->bInterfaceClass != + USB_CLASS_VENDOR_SPEC) { + best = c; break; } + + /* If all the remaining configs are vendor-specific, + * choose the first one. */ + else if (!best) + best = c; + } + + if (best) { + i = best->desc.bConfigurationValue; dev_info(&udev->dev, - "configuration #%d chosen from %d choices\n", - c, udev->descriptor.bNumConfigurations); + "configuration #%d chosen from %d choice%s\n", + i, num_configs, plural(num_configs)); + } else { + i = -1; + dev_warn(&udev->dev, + "no configuration chosen from %d choice%s\n", + num_configs, plural(num_configs)); } - return c; + return i; } #ifdef DEBUG @@ -1210,8 +1274,8 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) * * This is called with devices which have been enumerated, but not yet * configured. The device descriptor is available, but not descriptors - * for any device configuration. The caller must have locked udev and - * either the parent hub (if udev is a normal device) or else the + * for any device configuration. The caller must have locked either + * the parent hub (if udev is a normal device) or else the * usb_bus_list_lock (if udev is a root hub). The parent's pointer to * udev has already been installed, but udev is not yet visible through * sysfs or other filesystem code. @@ -1221,8 +1285,7 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) * * This call is synchronous, and may not be used in an interrupt context. * - * Only the hub driver should ever call this; root hub registration - * uses it indirectly. + * Only the hub driver or root-hub registrar should ever call this. */ int usb_new_device(struct usb_device *udev) { @@ -1269,15 +1332,9 @@ int usb_new_device(struct usb_device *udev) le16_to_cpu(udev->config[0].desc.wTotalLength), USB_DT_OTG, (void **) &desc) == 0) { if (desc->bmAttributes & USB_OTG_HNP) { - unsigned port1; + unsigned port1 = udev->portnum; struct usb_device *root = udev->parent; - for (port1 = 1; port1 <= root->maxchild; - port1++) { - if (root->children[port1-1] == udev) - break; - } - dev_info(&udev->dev, "Dual-Role OTG device on %sHNP port\n", (port1 == bus->otg_port) @@ -1331,27 +1388,27 @@ int usb_new_device(struct usb_device *udev) } usb_create_sysfs_dev_files (udev); + usb_lock_device(udev); + /* choose and set the configuration. that registers the interfaces * with the driver core, and lets usb device drivers bind to them. */ c = choose_configuration(udev); - if (c < 0) - dev_warn(&udev->dev, - "can't choose an initial configuration\n"); - else { + if (c >= 0) { err = usb_set_configuration(udev, c); if (err) { dev_err(&udev->dev, "can't set config #%d, error %d\n", c, err); - usb_remove_sysfs_dev_files(udev); - device_del(&udev->dev); - goto fail; + /* This need not be fatal. The user can try to + * set other configurations. */ } } /* USB device state == configured ... usable */ usb_notify_add_device(udev); + usb_unlock_device(udev); + return 0; fail: @@ -1654,15 +1711,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) int usb_suspend_device(struct usb_device *udev) { #ifdef CONFIG_USB_SUSPEND - int port1, status; - - port1 = locktree(udev); - if (port1 < 0) - return port1; - - status = __usb_suspend_device(udev, port1); - usb_unlock_device(udev); - return status; + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + return __usb_suspend_device(udev, udev->portnum); #else /* NOTE: udev->state unchanged, it's not lying ... */ udev->dev.power.power_state = PMSG_SUSPEND; @@ -1694,13 +1745,14 @@ static int finish_device_resume(struct usb_device *udev) usb_set_device_state(udev, udev->actconfig ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); + 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, * and device drivers will know about any resume quirks. */ status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); - if (status < 0) + if (status < 2) dev_dbg(&udev->dev, "gone after usb resume? status %d\n", status); @@ -1709,7 +1761,7 @@ static int finish_device_resume(struct usb_device *udev) int (*resume)(struct device *); le16_to_cpus(&devstatus); - if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP) + if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) && udev->parent) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), @@ -1729,8 +1781,14 @@ static int finish_device_resume(struct usb_device *udev) * may have a child resume event to deal with soon */ resume = udev->dev.bus->resume; - for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) - (void) resume(&udev->actconfig->interface[i]->dev); + for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { + struct device *dev = + &udev->actconfig->interface[i]->dev; + + down(&dev->sem); + (void) resume(dev); + up(&dev->sem); + } status = 0; } else if (udev->devnum <= 0) { @@ -1813,11 +1871,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) */ int usb_resume_device(struct usb_device *udev) { - int port1, status; + int status; - port1 = locktree(udev); - if (port1 < 0) - return port1; + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; #ifdef CONFIG_USB_SUSPEND /* selective resume of one downstream hub-to-device port */ @@ -1826,7 +1883,7 @@ int usb_resume_device(struct usb_device *udev) // NOTE swsusp may bork us, device state being wrong... // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), - port1, udev); + udev->portnum, udev); } else status = 0; } else @@ -1836,13 +1893,11 @@ int usb_resume_device(struct usb_device *udev) dev_dbg(&udev->dev, "can't resume, status %d\n", status); - usb_unlock_device(udev); - /* rebind drivers that had no suspend() */ if (status == 0) { - usb_lock_all_devices(); + usb_unlock_device(udev); bus_rescan_devices(&usb_bus_type); - usb_unlock_all_devices(); + usb_lock_device(udev); } return status; } @@ -1856,14 +1911,14 @@ static int remote_wakeup(struct usb_device *udev) /* don't repeat RESUME sequence if this device * was already woken up by some other task */ - down(&udev->serialize); + usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "RESUME (wakeup)\n"); /* TRSMRCY = 10 msec */ msleep(10); status = finish_device_resume(udev); } - up(&udev->serialize); + usb_unlock_device(udev); #endif return status; } @@ -1964,7 +2019,7 @@ static int hub_resume(struct usb_interface *intf) if (!udev || status < 0) continue; - down (&udev->serialize); + usb_lock_device(udev); if (portstat & USB_PORT_STAT_SUSPEND) status = hub_port_resume(hub, port1, udev); else { @@ -1975,7 +2030,7 @@ static int hub_resume(struct usb_interface *intf) hub_port_logical_disconnect(hub, port1); } } - up(&udev->serialize); + usb_unlock_device(udev); } } #endif @@ -2359,39 +2414,36 @@ hub_power_remaining (struct usb_hub *hub) { struct usb_device *hdev = hub->hdev; int remaining; - unsigned i; + int port1; - remaining = hub->power_budget; - if (!remaining) /* self-powered */ + if (!hub->limited_power) return 0; - for (i = 0; i < hdev->maxchild; i++) { - struct usb_device *udev = hdev->children[i]; - int delta, ceiling; + remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent; + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + struct usb_device *udev = hdev->children[port1 - 1]; + int delta; if (!udev) continue; - /* 100mA per-port ceiling, or 8mA for OTG ports */ - if (i != (udev->bus->otg_port - 1) || hdev->parent) - ceiling = 50; - else - ceiling = 4; - + /* Unconfigured devices may not use more than 100mA, + * or 8mA for OTG ports */ if (udev->actconfig) - delta = udev->actconfig->desc.bMaxPower; + delta = udev->actconfig->desc.bMaxPower * 2; + else if (port1 != udev->bus->otg_port || hdev->parent) + delta = 100; else - delta = ceiling; - // dev_dbg(&udev->dev, "budgeted %dmA\n", 2 * delta); - if (delta > ceiling) - dev_warn(&udev->dev, "%dmA over %dmA budget!\n", - 2 * (delta - ceiling), 2 * ceiling); + delta = 8; + if (delta > hub->mA_per_port) + dev_warn(&udev->dev, "%dmA is over %umA budget " + "for port %d!\n", + delta, hub->mA_per_port, port1); remaining -= delta; } if (remaining < 0) { - dev_warn(hub->intfdev, - "%dmA over power budget!\n", - -2 * remaining); + dev_warn(hub->intfdev, "%dmA over power budget!\n", + - remaining); remaining = 0; } return remaining; @@ -2486,7 +2538,8 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, usb_set_device_state(udev, USB_STATE_POWERED); udev->speed = USB_SPEED_UNKNOWN; - + udev->bus_mA = hub->mA_per_port; + /* set the address */ choose_address(udev); if (udev->devnum <= 0) { @@ -2506,16 +2559,16 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, * on the parent. */ if (udev->descriptor.bDeviceClass == USB_CLASS_HUB - && hub->power_budget) { + && udev->bus_mA <= 100) { u16 devstat; status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstat); - if (status < 0) { + if (status < 2) { dev_dbg(&udev->dev, "get status %d ?\n", status); goto loop_disable; } - cpu_to_le16s(&devstat); + le16_to_cpus(&devstat); if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) { dev_err(&udev->dev, "can't connect bus-powered hub " @@ -2540,7 +2593,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, * udev becomes globally accessible, although presumably * no one will look at it until hdev is unlocked. */ - down (&udev->serialize); status = 0; /* We mustn't add new devices if the parent hub has @@ -2564,15 +2616,12 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, } } - up (&udev->serialize); if (status) goto loop_disable; status = hub_power_remaining(hub); if (status) - dev_dbg(hub_dev, - "%dmA power budget left\n", - 2 * status); + dev_dbg(hub_dev, "%dmA power budget left\n", status); return; @@ -2648,6 +2697,8 @@ static void hub_events(void) if (i) { dpm_runtime_resume(&hdev->dev); dpm_runtime_resume(&intf->dev); + usb_put_intf(intf); + continue; } /* Lock the device, then check to see if we were @@ -2661,7 +2712,7 @@ static void hub_events(void) /* If the hub has died, clean up after it */ if (hdev->state == USB_STATE_NOTATTACHED) { - hub_pre_reset(hub); + hub_pre_reset(hub, 0); goto loop; } @@ -2784,6 +2835,11 @@ static void hub_events(void) if (hubchange & HUB_CHANGE_LOCAL_POWER) { dev_dbg (hub_dev, "power change\n"); clear_hub_feature(hdev, C_HUB_LOCAL_POWER); + if (hubstatus & HUB_STATUS_LOCAL_POWER) + /* FIXME: Is this always true? */ + hub->limited_power = 0; + else + hub->limited_power = 1; } if (hubchange & HUB_CHANGE_OVERCURRENT) { dev_dbg (hub_dev, "overcurrent change\n"); @@ -2832,7 +2888,6 @@ static struct usb_device_id hub_id_table [] = { MODULE_DEVICE_TABLE (usb, hub_id_table); static struct usb_driver hub_driver = { - .owner = THIS_MODULE, .name = "hub", .probe = hub_probe, .disconnect = hub_disconnect, @@ -2944,7 +2999,8 @@ int usb_reset_device(struct usb_device *udev) struct usb_hub *parent_hub; struct usb_device_descriptor descriptor = udev->descriptor; struct usb_hub *hub = NULL; - int i, ret = 0, port1 = -1; + int i, ret = 0; + int port1 = udev->portnum; if (udev->state == USB_STATE_NOTATTACHED || udev->state == USB_STATE_SUSPENDED) { @@ -2958,18 +3014,6 @@ int usb_reset_device(struct usb_device *udev) dev_dbg(&udev->dev, "%s for root hub!\n", __FUNCTION__); return -EISDIR; } - - for (i = 0; i < parent_hdev->maxchild; i++) - if (parent_hdev->children[i] == udev) { - port1 = i + 1; - break; - } - - if (port1 < 0) { - /* If this ever happens, it's very bad */ - dev_err(&udev->dev, "Can't locate device's port!\n"); - return -ENOENT; - } parent_hub = hdev_to_hub(parent_hdev); /* If we're resetting an active hub, take some special actions */ @@ -2977,7 +3021,7 @@ int usb_reset_device(struct usb_device *udev) udev->actconfig->interface[0]->dev.driver == &hub_driver.driver && (hub = hdev_to_hub(udev)) != NULL) { - hub_pre_reset(hub); + hub_pre_reset(hub, 0); } set_bit(port1, parent_hub->busy_bits); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index bf23f897802..29d5f45a845 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -220,8 +220,9 @@ struct usb_hub { struct usb_hub_descriptor *descriptor; /* class descriptor */ struct usb_tt tt; /* Transaction Translator */ - u8 power_budget; /* in 2mA units; or zero */ + unsigned mA_per_port; /* current for each child */ + unsigned limited_power:1; unsigned quiescing:1; unsigned activating:1; unsigned resume_root_hub:1; diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index fe74f99ca5f..319de03944e 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1387,6 +1387,12 @@ free_interfaces: if (dev->state != USB_STATE_ADDRESS) usb_disable_device (dev, 1); // Skip ep0 + i = dev->bus_mA - cp->desc.bMaxPower * 2; + if (i < 0) + dev_warn(&dev->dev, "new config #%d exceeds power " + "limit by %dmA\n", + configuration, -i); + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e80ef946782..56a3520863a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -32,7 +32,6 @@ #include <linux/spinlock.h> #include <linux/errno.h> #include <linux/smp_lock.h> -#include <linux/rwsem.h> #include <linux/usb.h> #include <asm/io.h> @@ -47,165 +46,7 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ - /* Not honored on modular build */ -static DECLARE_RWSEM(usb_all_devices_rwsem); - - -static int generic_probe (struct device *dev) -{ - return 0; -} -static int generic_remove (struct device *dev) -{ - struct usb_device *udev = to_usb_device(dev); - - /* if this is only an unbind, not a physical disconnect, then - * unconfigure the device */ - if (udev->state == USB_STATE_CONFIGURED) - usb_set_configuration(udev, 0); - - /* in case the call failed or the device was suspended */ - if (udev->state >= USB_STATE_CONFIGURED) - usb_disable_device(udev, 0); - return 0; -} - -static struct device_driver usb_generic_driver = { - .owner = THIS_MODULE, - .name = "usb", - .bus = &usb_bus_type, - .probe = generic_probe, - .remove = generic_remove, -}; - -static int usb_generic_driver_data; - -/* called from driver core with usb_bus_type.subsys writelock */ -static int usb_probe_interface(struct device *dev) -{ - struct usb_interface * intf = to_usb_interface(dev); - struct usb_driver * driver = to_usb_driver(dev->driver); - const struct usb_device_id *id; - int error = -ENODEV; - - dev_dbg(dev, "%s\n", __FUNCTION__); - - if (!driver->probe) - return error; - /* FIXME we'd much prefer to just resume it ... */ - if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; - - id = usb_match_id (intf, driver->id_table); - if (id) { - dev_dbg (dev, "%s - got id\n", __FUNCTION__); - - /* Interface "power state" doesn't correspond to any hardware - * state whatsoever. We use it to record when it's bound to - * a driver that may start I/0: it's not frozen/quiesced. - */ - mark_active(intf); - intf->condition = USB_INTERFACE_BINDING; - error = driver->probe (intf, id); - if (error) { - mark_quiesced(intf); - intf->condition = USB_INTERFACE_UNBOUND; - } else - intf->condition = USB_INTERFACE_BOUND; - } - - return error; -} - -/* called from driver core with usb_bus_type.subsys writelock */ -static int usb_unbind_interface(struct device *dev) -{ - struct usb_interface *intf = to_usb_interface(dev); - struct usb_driver *driver = to_usb_driver(intf->dev.driver); - - intf->condition = USB_INTERFACE_UNBINDING; - - /* release all urbs for this interface */ - usb_disable_interface(interface_to_usbdev(intf), intf); - - if (driver && driver->disconnect) - driver->disconnect(intf); - - /* reset other interface state */ - usb_set_interface(interface_to_usbdev(intf), - intf->altsetting[0].desc.bInterfaceNumber, - 0); - usb_set_intfdata(intf, NULL); - intf->condition = USB_INTERFACE_UNBOUND; - mark_quiesced(intf); - - return 0; -} - -/** - * usb_register - register a USB driver - * @new_driver: USB operations for the driver - * - * Registers a USB driver with the USB core. The list of unattached - * interfaces will be rescanned whenever a new driver is added, allowing - * the new driver to attach to any recognized devices. - * Returns a negative error code on failure and 0 on success. - * - * NOTE: if you want your driver to use the USB major number, you must call - * usb_register_dev() to enable that functionality. This function no longer - * takes care of that. - */ -int usb_register(struct usb_driver *new_driver) -{ - int retval = 0; - - if (nousb) - return -ENODEV; - - new_driver->driver.name = (char *)new_driver->name; - new_driver->driver.bus = &usb_bus_type; - new_driver->driver.probe = usb_probe_interface; - new_driver->driver.remove = usb_unbind_interface; - new_driver->driver.owner = new_driver->owner; - - usb_lock_all_devices(); - retval = driver_register(&new_driver->driver); - usb_unlock_all_devices(); - - if (!retval) { - pr_info("%s: registered new driver %s\n", - usbcore_name, new_driver->name); - usbfs_update_special(); - } else { - printk(KERN_ERR "%s: error %d registering driver %s\n", - usbcore_name, retval, new_driver->name); - } - - return retval; -} - -/** - * usb_deregister - unregister a USB driver - * @driver: USB operations of the driver to unregister - * Context: must be able to sleep - * - * Unlinks the specified driver from the internal USB driver list. - * - * NOTE: If you called usb_register_dev(), you still need to call - * usb_deregister_dev() to clean up your driver's allocated minor numbers, - * this * call will no longer do it for you. - */ -void usb_deregister(struct usb_driver *driver) -{ - pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name); - - usb_lock_all_devices(); - driver_unregister (&driver->driver); - usb_unlock_all_devices(); - - usbfs_update_special(); -} /** * usb_ifnum_to_if - get the interface object with a given interface number @@ -351,152 +192,23 @@ void usb_driver_release_interface(struct usb_driver *driver, iface->condition = USB_INTERFACE_UNBOUND; mark_quiesced(iface); } - -/** - * usb_match_id - find first usb_device_id matching device or interface - * @interface: the interface of interest - * @id: array of usb_device_id structures, terminated by zero entry - * - * usb_match_id searches an array of usb_device_id's and returns - * the first one matching the device or interface, or null. - * This is used when binding (or rebinding) a driver to an interface. - * Most USB device drivers will use this indirectly, through the usb core, - * but some layered driver frameworks use it directly. - * These device tables are exported with MODULE_DEVICE_TABLE, through - * modutils and "modules.usbmap", to support the driver loading - * functionality of USB hotplugging. - * - * What Matches: - * - * The "match_flags" element in a usb_device_id controls which - * members are used. If the corresponding bit is set, the - * value in the device_id must match its corresponding member - * in the device or interface descriptor, or else the device_id - * does not match. - * - * "driver_info" is normally used only by device drivers, - * but you can create a wildcard "matches anything" usb_device_id - * as a driver's "modules.usbmap" entry if you provide an id with - * only a nonzero "driver_info" field. If you do this, the USB device - * driver's probe() routine should use additional intelligence to - * decide whether to bind to the specified interface. - * - * What Makes Good usb_device_id Tables: - * - * The match algorithm is very simple, so that intelligence in - * driver selection must come from smart driver id records. - * Unless you have good reasons to use another selection policy, - * provide match elements only in related groups, and order match - * specifiers from specific to general. Use the macros provided - * for that purpose if you can. - * - * The most specific match specifiers use device descriptor - * data. These are commonly used with product-specific matches; - * the USB_DEVICE macro lets you provide vendor and product IDs, - * and you can also match against ranges of product revisions. - * These are widely used for devices with application or vendor - * specific bDeviceClass values. - * - * Matches based on device class/subclass/protocol specifications - * are slightly more general; use the USB_DEVICE_INFO macro, or - * its siblings. These are used with single-function devices - * where bDeviceClass doesn't specify that each interface has - * its own class. - * - * Matches based on interface class/subclass/protocol are the - * most general; they let drivers bind to any interface on a - * multiple-function device. Use the USB_INTERFACE_INFO - * macro, or its siblings, to match class-per-interface style - * devices (as recorded in bDeviceClass). - * - * Within those groups, remember that not all combinations are - * meaningful. For example, don't give a product version range - * without vendor and product IDs; or specify a protocol without - * its associated class and subclass. - */ -const struct usb_device_id * -usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) -{ - struct usb_host_interface *intf; - struct usb_device *dev; - - /* proc_connectinfo in devio.c may call us with id == NULL. */ - if (id == NULL) - return NULL; - - intf = interface->cur_altsetting; - dev = interface_to_usbdev(interface); - - /* It is important to check that id->driver_info is nonzero, - since an entry that is all zeroes except for a nonzero - id->driver_info is the way to create an entry that - indicates that the driver want to examine every - device and interface. */ - for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || - id->driver_info; id++) { - - if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && - id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && - id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) - continue; - - /* No need to test id->bcdDevice_lo != 0, since 0 is never - greater than any unsigned number. */ - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && - (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && - (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && - (id->bDeviceClass != dev->descriptor.bDeviceClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && - (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && - (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && - (id->bInterfaceClass != intf->desc.bInterfaceClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && - (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass)) - continue; - - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && - (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol)) - continue; - - return id; - } - - return NULL; -} - +struct find_interface_arg { + int minor; + struct usb_interface *interface; +}; static int __find_interface(struct device * dev, void * data) { - struct usb_interface ** ret = (struct usb_interface **)data; - struct usb_interface * intf = *ret; - int *minor = (int *)data; + struct find_interface_arg *arg = data; + struct usb_interface *intf; /* can't look at usb devices, only interfaces */ if (dev->driver == &usb_generic_driver) return 0; intf = to_usb_interface(dev); - if (intf->minor != -1 && intf->minor == *minor) { - *ret = intf; + if (intf->minor != -1 && intf->minor == arg->minor) { + arg->interface = intf; return 1; } return 0; @@ -513,35 +225,14 @@ static int __find_interface(struct device * dev, void * data) */ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) { - struct usb_interface *intf = (struct usb_interface *)(long)minor; - int ret; - - ret = driver_for_each_device(&drv->driver, NULL, &intf, __find_interface); - - return ret ? intf : NULL; -} - -static int usb_device_match (struct device *dev, struct device_driver *drv) -{ - struct usb_interface *intf; - struct usb_driver *usb_drv; - const struct usb_device_id *id; - - /* check for generic driver, which we don't match any device with */ - if (drv == &usb_generic_driver) - return 0; - - intf = to_usb_interface(dev); - usb_drv = to_usb_driver(drv); - - id = usb_match_id (intf, usb_drv->id_table); - if (id) - return 1; + struct find_interface_arg argb; - return 0; + argb.minor = minor; + argb.interface = NULL; + driver_for_each_device(&drv->driver, NULL, &argb, __find_interface); + return argb.interface; } - #ifdef CONFIG_HOTPLUG /* @@ -750,12 +441,11 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) /* hub driver sets up TT records */ } + dev->portnum = port1; dev->bus = bus; dev->parent = parent; INIT_LIST_HEAD(&dev->filelist); - init_MUTEX(&dev->serialize); - return dev; } @@ -828,76 +518,21 @@ void usb_put_intf(struct usb_interface *intf) /* USB device locking * - * Although locking USB devices should be straightforward, it is - * complicated by the way the driver-model core works. When a new USB - * driver is registered or unregistered, the core will automatically - * probe or disconnect all matching interfaces on all USB devices while - * holding the USB subsystem writelock. There's no good way for us to - * tell which devices will be used or to lock them beforehand; our only - * option is to effectively lock all the USB devices. - * - * We do that by using a private rw-semaphore, usb_all_devices_rwsem. - * When locking an individual device you must first acquire the rwsem's - * readlock. When a driver is registered or unregistered the writelock - * must be held. These actions are encapsulated in the subroutines - * below, so all a driver needs to do is call usb_lock_device() and - * usb_unlock_device(). + * USB devices and interfaces are locked using the semaphore in their + * embedded struct device. The hub driver guarantees that whenever a + * device is connected or disconnected, drivers are called with the + * USB device locked as well as their particular interface. * * Complications arise when several devices are to be locked at the same * time. Only hub-aware drivers that are part of usbcore ever have to - * do this; nobody else needs to worry about it. The problem is that - * usb_lock_device() must not be called to lock a second device since it - * would acquire the rwsem's readlock reentrantly, leading to deadlock if - * another thread was waiting for the writelock. The solution is simple: - * - * When locking more than one device, call usb_lock_device() - * to lock the first one. Lock the others by calling - * down(&udev->serialize) directly. - * - * When unlocking multiple devices, use up(&udev->serialize) - * to unlock all but the last one. Unlock the last one by - * calling usb_unlock_device(). + * do this; nobody else needs to worry about it. The rule for locking + * is simple: * * When locking both a device and its parent, always lock the * the parent first. */ /** - * usb_lock_device - acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Use this routine when you don't hold any other device locks; - * to acquire nested inner locks call down(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_lock_device(struct usb_device *udev) -{ - down_read(&usb_all_devices_rwsem); - down(&udev->serialize); -} - -/** - * usb_trylock_device - attempt to acquire the lock for a usb device structure - * @udev: device that's being locked - * - * Don't use this routine if you already hold a device lock; - * use down_trylock(&udev->serialize) instead. - * This is necessary for proper interaction with usb_lock_all_devices(). - * - * Returns 1 if successful, 0 if contention. - */ -int usb_trylock_device(struct usb_device *udev) -{ - if (!down_read_trylock(&usb_all_devices_rwsem)) - return 0; - if (down_trylock(&udev->serialize)) { - up_read(&usb_all_devices_rwsem); - return 0; - } - return 1; -} - -/** * usb_lock_device_for_reset - cautiously acquire the lock for a * usb device structure * @udev: device that's being locked @@ -935,7 +570,7 @@ int usb_lock_device_for_reset(struct usb_device *udev, } } - while (!usb_trylock_device(udev)) { + while (usb_trylock_device(udev) != 0) { /* If we can't acquire the lock after waiting one second, * we're probably deadlocked */ @@ -953,39 +588,6 @@ int usb_lock_device_for_reset(struct usb_device *udev, return 1; } -/** - * usb_unlock_device - release the lock for a usb device structure - * @udev: device that's being unlocked - * - * Use this routine when releasing the only device lock you hold; - * to release inner nested locks call up(&udev->serialize) directly. - * This is necessary for proper interaction with usb_lock_all_devices(). - */ -void usb_unlock_device(struct usb_device *udev) -{ - up(&udev->serialize); - up_read(&usb_all_devices_rwsem); -} - -/** - * usb_lock_all_devices - acquire the lock for all usb device structures - * - * This is necessary when registering a new driver or probing a bus, - * since the driver-model core may try to use any usb_device. - */ -void usb_lock_all_devices(void) -{ - down_write(&usb_all_devices_rwsem); -} - -/** - * usb_unlock_all_devices - release the lock for all usb device structures - */ -void usb_unlock_all_devices(void) -{ - up_write(&usb_all_devices_rwsem); -} - static struct usb_device *match_device(struct usb_device *dev, u16 vendor_id, u16 product_id) @@ -1008,10 +610,10 @@ static struct usb_device *match_device(struct usb_device *dev, /* look through all of the children of this device */ for (child = 0; child < dev->maxchild; ++child) { if (dev->children[child]) { - down(&dev->children[child]->serialize); + usb_lock_device(dev->children[child]); ret_dev = match_device(dev->children[child], vendor_id, product_id); - up(&dev->children[child]->serialize); + usb_unlock_device(dev->children[child]); if (ret_dev) goto exit; } @@ -1496,18 +1098,8 @@ struct bus_type usb_bus_type = { .resume = usb_generic_resume, }; -#ifndef MODULE - -static int __init usb_setup_disable(char *str) -{ - nousb = 1; - return 1; -} - /* format to disable USB on kernel command line is: nousb */ -__setup("nousb", usb_setup_disable); - -#endif +__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444); /* * for external read access to <nousb> @@ -1598,8 +1190,6 @@ module_exit(usb_exit); * driver modules to use. */ -EXPORT_SYMBOL(usb_register); -EXPORT_SYMBOL(usb_deregister); EXPORT_SYMBOL(usb_disabled); EXPORT_SYMBOL_GPL(usb_get_intf); @@ -1610,14 +1200,10 @@ EXPORT_SYMBOL(usb_put_dev); EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); -EXPORT_SYMBOL(usb_lock_device); -EXPORT_SYMBOL(usb_trylock_device); EXPORT_SYMBOL(usb_lock_device_for_reset); -EXPORT_SYMBOL(usb_unlock_device); EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_driver_release_interface); -EXPORT_SYMBOL(usb_match_id); EXPORT_SYMBOL(usb_find_interface); EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_altnum_to_altsetting); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1c4a68499dc..4647e1ebc68 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -16,9 +16,6 @@ extern int usb_get_device_descriptor(struct usb_device *dev, extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); -extern void usb_lock_all_devices(void); -extern void usb_unlock_all_devices(void); - extern void usb_kick_khubd(struct usb_device *dev); extern void usb_suspend_root_hub(struct usb_device *hdev); extern void usb_resume_root_hub(struct usb_device *dev); @@ -33,6 +30,9 @@ extern void usb_host_cleanup(void); extern int usb_suspend_device(struct usb_device *dev); extern int usb_resume_device(struct usb_device *dev); +extern struct device_driver usb_generic_driver; +extern int usb_generic_driver_data; +extern int usb_device_match(struct device *dev, struct device_driver *drv); /* Interfaces and their "power state" are owned by usbcore */ diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index c655d46c8ae..9734cb76dd6 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -138,7 +138,7 @@ static const char *const ep_name [] = { /* or like sa1100: two fixed function endpoints */ "ep1out-bulk", "ep2in-bulk", }; -#define DUMMY_ENDPOINTS (sizeof(ep_name)/sizeof(char *)) +#define DUMMY_ENDPOINTS ARRAY_SIZE(ep_name) /*-------------------------------------------------------------------------*/ @@ -896,7 +896,7 @@ dummy_gadget_release (struct device *dev) #endif } -static int dummy_udc_probe (struct platform_device *dev) +static int dummy_udc_probe (struct platform_device *pdev) { struct dummy *dum = the_controller; int rc; @@ -909,7 +909,7 @@ static int dummy_udc_probe (struct platform_device *dev) dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0); strcpy (dum->gadget.dev.bus_id, "gadget"); - dum->gadget.dev.parent = &dev->dev; + dum->gadget.dev.parent = &pdev->dev; dum->gadget.dev.release = dummy_gadget_release; rc = device_register (&dum->gadget.dev); if (rc < 0) @@ -919,47 +919,47 @@ static int dummy_udc_probe (struct platform_device *dev) usb_bus_get (&dummy_to_hcd (dum)->self); #endif - platform_set_drvdata (dev, dum); + platform_set_drvdata (pdev, dum); device_create_file (&dum->gadget.dev, &dev_attr_function); return rc; } -static int dummy_udc_remove (struct platform_device *dev) +static int dummy_udc_remove (struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata (dev); + struct dummy *dum = platform_get_drvdata (pdev); - platform_set_drvdata (dev, NULL); + platform_set_drvdata (pdev, NULL); device_remove_file (&dum->gadget.dev, &dev_attr_function); device_unregister (&dum->gadget.dev); return 0; } -static int dummy_udc_suspend (struct platform_device *dev, pm_message_t state) +static int dummy_udc_suspend (struct platform_device *pdev, pm_message_t state) { - struct dummy *dum = platform_get_drvdata(dev); + struct dummy *dum = platform_get_drvdata(pdev); - dev_dbg (&dev->dev, "%s\n", __FUNCTION__); + dev_dbg (&pdev->dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 1; set_link_state (dum); spin_unlock_irq (&dum->lock); - dev->dev.power.power_state = state; + pdev->dev.power.power_state = state; usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } -static int dummy_udc_resume (struct platform_device *dev) +static int dummy_udc_resume (struct platform_device *pdev) { - struct dummy *dum = platform_get_drvdata(dev); + struct dummy *dum = platform_get_drvdata(pdev); - dev_dbg (&dev->dev, "%s\n", __FUNCTION__); + dev_dbg (&pdev->dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); dum->udc_suspended = 0; set_link_state (dum); spin_unlock_irq (&dum->lock); - dev->dev.power.power_state = PMSG_ON; + pdev->dev.power.power_state = PMSG_ON; usb_hcd_poll_rh_status (dummy_to_hcd (dum)); return 0; } @@ -1576,7 +1576,7 @@ static int dummy_hub_status (struct usb_hcd *hcd, char *buf) dum = hcd_to_dummy (hcd); spin_lock_irqsave (&dum->lock, flags); - if (hcd->state != HC_STATE_RUNNING) + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) goto done; if (dum->resuming && time_after_eq (jiffies, dum->re_timeout)) { @@ -1623,7 +1623,7 @@ static int dummy_hub_control ( int retval = 0; unsigned long flags; - if (hcd->state != HC_STATE_RUNNING) + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) return -ETIMEDOUT; dum = hcd_to_dummy (hcd); @@ -1756,9 +1756,12 @@ static int dummy_bus_suspend (struct usb_hcd *hcd) { struct dummy *dum = hcd_to_dummy (hcd); + dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__); + spin_lock_irq (&dum->lock); dum->rh_state = DUMMY_RH_SUSPENDED; set_link_state (dum); + hcd->state = HC_STATE_SUSPENDED; spin_unlock_irq (&dum->lock); return 0; } @@ -1766,14 +1769,23 @@ static int dummy_bus_suspend (struct usb_hcd *hcd) static int dummy_bus_resume (struct usb_hcd *hcd) { struct dummy *dum = hcd_to_dummy (hcd); + int rc = 0; + + dev_dbg (&hcd->self.root_hub->dev, "%s\n", __FUNCTION__); spin_lock_irq (&dum->lock); - dum->rh_state = DUMMY_RH_RUNNING; - set_link_state (dum); - if (!list_empty(&dum->urbp_list)) - mod_timer (&dum->timer, jiffies); + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + dev_warn (&hcd->self.root_hub->dev, "HC isn't running!\n"); + rc = -ENODEV; + } else { + dum->rh_state = DUMMY_RH_RUNNING; + set_link_state (dum); + if (!list_empty(&dum->urbp_list)) + mod_timer (&dum->timer, jiffies); + hcd->state = HC_STATE_RUNNING; + } spin_unlock_irq (&dum->lock); - return 0; + return rc; } /*-------------------------------------------------------------------------*/ @@ -1899,14 +1911,14 @@ static const struct hc_driver dummy_hcd = { .bus_resume = dummy_bus_resume, }; -static int dummy_hcd_probe (struct platform_device *dev) +static int dummy_hcd_probe(struct platform_device *pdev) { struct usb_hcd *hcd; int retval; - dev_info(&dev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); + dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); - hcd = usb_create_hcd (&dummy_hcd, &dev->dev, dev->dev.bus_id); + hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id); if (!hcd) return -ENOMEM; the_controller = hcd_to_dummy (hcd); @@ -1919,36 +1931,43 @@ static int dummy_hcd_probe (struct platform_device *dev) return retval; } -static int dummy_hcd_remove (struct platform_device *dev) +static int dummy_hcd_remove (struct platform_device *pdev) { struct usb_hcd *hcd; - hcd = platform_get_drvdata (dev); + hcd = platform_get_drvdata (pdev); usb_remove_hcd (hcd); usb_put_hcd (hcd); the_controller = NULL; return 0; } -static int dummy_hcd_suspend (struct platform_device *dev, pm_message_t state) +static int dummy_hcd_suspend (struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd; + struct dummy *dum; + int rc = 0; - dev_dbg (&dev->dev, "%s\n", __FUNCTION__); - hcd = platform_get_drvdata (dev); + dev_dbg (&pdev->dev, "%s\n", __FUNCTION__); - hcd->state = HC_STATE_SUSPENDED; - return 0; + hcd = platform_get_drvdata (pdev); + dum = hcd_to_dummy (hcd); + if (dum->rh_state == DUMMY_RH_RUNNING) { + dev_warn(&pdev->dev, "Root hub isn't suspended!\n"); + rc = -EBUSY; + } else + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + return rc; } -static int dummy_hcd_resume (struct platform_device *dev) +static int dummy_hcd_resume (struct platform_device *pdev) { struct usb_hcd *hcd; - dev_dbg (&dev->dev, "%s\n", __FUNCTION__); - hcd = platform_get_drvdata (dev); - hcd->state = HC_STATE_RUNNING; + dev_dbg (&pdev->dev, "%s\n", __FUNCTION__); + hcd = platform_get_drvdata (pdev); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); usb_hcd_poll_rh_status (hcd); return 0; } diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index ea09aaa3cab..0cea9782d7d 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -224,6 +224,7 @@ #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/kref.h> #include <linux/kthread.h> #include <linux/limits.h> #include <linux/list.h> @@ -238,7 +239,6 @@ #include <linux/string.h> #include <linux/suspend.h> #include <linux/utsname.h> -#include <linux/wait.h> #include <linux/usb_ch9.h> #include <linux/usb_gadget.h> @@ -250,7 +250,7 @@ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "20 October 2004" +#define DRIVER_VERSION "28 November 2005" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -335,8 +335,8 @@ MODULE_LICENSE("Dual BSD/GPL"); #define MAX_LUNS 8 /* Arggh! There should be a module_param_array_named macro! */ -static char *file[MAX_LUNS] = {NULL, }; -static int ro[MAX_LUNS] = {0, }; +static char *file[MAX_LUNS]; +static int ro[MAX_LUNS]; static struct { int num_filenames; @@ -587,7 +587,7 @@ enum fsg_buffer_state { struct fsg_buffhd { void *buf; dma_addr_t dma; - volatile enum fsg_buffer_state state; + enum fsg_buffer_state state; struct fsg_buffhd *next; /* The NetChip 2280 is faster, and handles some protocol faults @@ -596,9 +596,9 @@ struct fsg_buffhd { unsigned int bulk_out_intended_length; struct usb_request *inreq; - volatile int inreq_busy; + int inreq_busy; struct usb_request *outreq; - volatile int outreq_busy; + int outreq_busy; }; enum fsg_state { @@ -631,13 +631,16 @@ struct fsg_dev { /* filesem protects: backing files in use */ struct rw_semaphore filesem; + /* reference counting: wait until all LUNs are released */ + struct kref ref; + struct usb_ep *ep0; // Handy copy of gadget->ep0 struct usb_request *ep0req; // For control responses - volatile unsigned int ep0_req_tag; + unsigned int ep0_req_tag; const char *ep0req_name; struct usb_request *intreq; // For interrupt responses - volatile int intreq_busy; + int intreq_busy; struct fsg_buffhd *intr_buffhd; unsigned int bulk_out_maxpacket; @@ -667,7 +670,6 @@ struct fsg_dev { struct fsg_buffhd *next_buffhd_to_drain; struct fsg_buffhd buffhds[NUM_BUFFERS]; - wait_queue_head_t thread_wqh; int thread_wakeup_needed; struct completion thread_notifier; struct task_struct *thread_task; @@ -694,7 +696,6 @@ struct fsg_dev { unsigned int nluns; struct lun *luns; struct lun *curlun; - struct completion lun_released; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -1073,11 +1074,13 @@ static int populate_config_buf(struct usb_gadget *gadget, /* These routines may be called in process context or in_irq */ +/* Caller must hold fsg->lock */ static void wakeup_thread(struct fsg_dev *fsg) { /* Tell the main thread that something has happened */ fsg->thread_wakeup_needed = 1; - wake_up_all(&fsg->thread_wqh); + if (fsg->thread_task) + wake_up_process(fsg->thread_task); } @@ -1164,11 +1167,12 @@ static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ + smp_wmb(); spin_lock(&fsg->lock); bh->inreq_busy = 0; bh->state = BUF_STATE_EMPTY; - spin_unlock(&fsg->lock); wakeup_thread(fsg); + spin_unlock(&fsg->lock); } static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) @@ -1185,11 +1189,12 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ + smp_wmb(); spin_lock(&fsg->lock); bh->outreq_busy = 0; bh->state = BUF_STATE_FULL; - spin_unlock(&fsg->lock); wakeup_thread(fsg); + spin_unlock(&fsg->lock); } @@ -1206,11 +1211,12 @@ static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) usb_ep_fifo_flush(ep); /* Hold the lock while we update the request and buffer states */ + smp_wmb(); spin_lock(&fsg->lock); fsg->intreq_busy = 0; bh->state = BUF_STATE_EMPTY; - spin_unlock(&fsg->lock); wakeup_thread(fsg); + spin_unlock(&fsg->lock); } #else @@ -1261,8 +1267,8 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) fsg->cbbuf_cmnd_size = req->actual; memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); - spin_unlock(&fsg->lock); wakeup_thread(fsg); + spin_unlock(&fsg->lock); } #else @@ -1514,8 +1520,8 @@ static int fsg_setup(struct usb_gadget *gadget, /* Use this for bulk or interrupt transfers, not ep0 */ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, - struct usb_request *req, volatile int *pbusy, - volatile enum fsg_buffer_state *state) + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) { int rc; @@ -1523,8 +1529,11 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, dump_msg(fsg, "bulk-in", req->buf, req->length); else if (ep == fsg->intr_in) dump_msg(fsg, "intr-in", req->buf, req->length); + + spin_lock_irq(&fsg->lock); *pbusy = 1; *state = BUF_STATE_BUSY; + spin_unlock_irq(&fsg->lock); rc = usb_ep_queue(ep, req, GFP_KERNEL); if (rc != 0) { *pbusy = 0; @@ -1544,14 +1553,23 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, static int sleep_thread(struct fsg_dev *fsg) { - int rc; + int rc = 0; /* Wait until a signal arrives or we are woken up */ - rc = wait_event_interruptible(fsg->thread_wqh, - fsg->thread_wakeup_needed); + for (;;) { + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + if (fsg->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); fsg->thread_wakeup_needed = 0; - try_to_freeze(); - return (rc ? -EINTR : 0); + return rc; } @@ -1788,6 +1806,7 @@ static int do_write(struct fsg_dev *fsg) if (bh->state == BUF_STATE_EMPTY && !get_some_more) break; // We stopped early if (bh->state == BUF_STATE_FULL) { + smp_rmb(); fsg->next_buffhd_to_drain = bh->next; bh->state = BUF_STATE_EMPTY; @@ -2356,6 +2375,7 @@ static int throw_away_data(struct fsg_dev *fsg) /* Throw away the data in a filled buffer */ if (bh->state == BUF_STATE_FULL) { + smp_rmb(); bh->state = BUF_STATE_EMPTY; fsg->next_buffhd_to_drain = bh->next; @@ -3021,6 +3041,7 @@ static int get_next_command(struct fsg_dev *fsg) if ((rc = sleep_thread(fsg)) != 0) return rc; } + smp_rmb(); rc = received_cbw(fsg, bh); bh->state = BUF_STATE_EMPTY; @@ -3642,11 +3663,19 @@ static DEVICE_ATTR(file, 0444, show_file, NULL); /*-------------------------------------------------------------------------*/ +static void fsg_release(struct kref *ref) +{ + struct fsg_dev *fsg = container_of(ref, struct fsg_dev, ref); + + kfree(fsg->luns); + kfree(fsg); +} + static void lun_release(struct device *dev) { struct fsg_dev *fsg = (struct fsg_dev *) dev_get_drvdata(dev); - complete(&fsg->lun_released); + kref_put(&fsg->ref, fsg_release); } static void fsg_unbind(struct usb_gadget *gadget) @@ -3660,14 +3689,12 @@ static void fsg_unbind(struct usb_gadget *gadget) clear_bit(REGISTERED, &fsg->atomic_bitflags); /* Unregister the sysfs attribute files and the LUNs */ - init_completion(&fsg->lun_released); for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; if (curlun->registered) { device_remove_file(&curlun->dev, &dev_attr_ro); device_remove_file(&curlun->dev, &dev_attr_file); device_unregister(&curlun->dev); - wait_for_completion(&fsg->lun_released); curlun->registered = 0; } } @@ -3846,6 +3873,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) curlun->dev.release = lun_release; device_create_file(&curlun->dev, &dev_attr_ro); device_create_file(&curlun->dev, &dev_attr_file); + kref_get(&fsg->ref); } if (file[i] && *file[i]) { @@ -4061,7 +4089,7 @@ static int __init fsg_alloc(void) return -ENOMEM; spin_lock_init(&fsg->lock); init_rwsem(&fsg->filesem); - init_waitqueue_head(&fsg->thread_wqh); + kref_init(&fsg->ref); init_completion(&fsg->thread_notifier); the_fsg = fsg; @@ -4069,13 +4097,6 @@ static int __init fsg_alloc(void) } -static void fsg_free(struct fsg_dev *fsg) -{ - kfree(fsg->luns); - kfree(fsg); -} - - static int __init fsg_init(void) { int rc; @@ -4085,7 +4106,7 @@ static int __init fsg_init(void) return rc; fsg = the_fsg; if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) - fsg_free(fsg); + kref_put(&fsg->ref, fsg_release); return rc; } module_init(fsg_init); @@ -4103,6 +4124,6 @@ static void __exit fsg_cleanup(void) wait_for_completion(&fsg->thread_notifier); close_all_backing_files(fsg); - fsg_free(fsg); + kref_put(&fsg->ref, fsg_release); } module_exit(fsg_cleanup); diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index b35ac6d334f..65e084a2c87 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -890,10 +890,12 @@ static void gs_close(struct tty_struct *tty, struct file *file) /* wait for write buffer to drain, or */ /* at most GS_CLOSE_TIMEOUT seconds */ if (gs_buf_data_avail(port->port_write_buf) > 0) { + spin_unlock_irqrestore(&port->port_lock, flags); wait_cond_interruptible_timeout(port->port_write_wait, port->port_dev == NULL || gs_buf_data_avail(port->port_write_buf) == 0, &port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ); + spin_lock_irqsave(&port->port_lock, flags); } /* free disconnected port on final close */ diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 58321d3f314..e3020f4b17b 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -2,6 +2,10 @@ # Makefile for USB Host Controller Drivers # +ifeq ($(CONFIG_USB_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + obj-$(CONFIG_PCI) += pci-quirks.o obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 29f52a44b92..9dd3d14c64f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -17,13 +17,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_DEBUG - #define DEBUG -#else - #undef DEBUG -#endif - #include <linux/module.h> #include <linux/pci.h> #include <linux/dmapool.h> @@ -624,7 +617,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) } /* remote wakeup [4.3.1] */ - if ((status & STS_PCD) && hcd->remote_wakeup) { + if (status & STS_PCD) { unsigned i = HCS_N_PORTS (ehci->hcs_params); /* resume root hub? */ diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 82caf336e9b..69b0b9be7a6 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -59,7 +59,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) t2 |= PORT_SUSPEND; - if (hcd->remote_wakeup) + if (device_may_wakeup(&hcd->self.root_hub->dev)) t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; else t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); @@ -517,7 +517,7 @@ static int ehci_hub_control ( if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; - if (hcd->remote_wakeup) + if (device_may_wakeup(&hcd->self.root_hub->dev)) temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 13f73a836e4..08ca0f849da 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -210,7 +210,16 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); - /* REVISIT: per-port wake capability (PCI 0x62) currently unused */ + /* Workaround current PCI init glitch: wakeup bits aren't + * being set from PCI PM capability. + */ + if (!device_can_wakeup(&pdev->dev)) { + u16 port_wake; + + pci_read_config_word(pdev, 0x62, &port_wake); + if (port_wake & 0x0001) + device_init_wakeup(&pdev->dev, 1); + } retval = ehci_pci_reinit(ehci, pdev); done: @@ -269,7 +278,6 @@ static int ehci_pci_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned port; - struct usb_device *root = hcd->self.root_hub; struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval = -EINVAL; @@ -303,13 +311,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd) restart: ehci_dbg(ehci, "lost power, restarting\n"); - for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) { - port--; - if (!root->children [port]) - continue; - usb_set_device_state(root->children[port], - USB_STATE_NOTATTACHED); - } + usb_root_hub_lost_power(hcd->self.root_hub); /* Else reset, to cope with power loss or flush-to-storage * style "resume" having let BIOS kick in during reboot. diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index bf03ec0d8ee..9b13bf2fa98 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -514,18 +514,18 @@ qh_urb_transaction ( qtd->urb = urb; qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma); list_add_tail (&qtd->qtd_list, head); + + /* for zero length DATA stages, STATUS is always IN */ + if (len == 0) + token |= (1 /* "in" */ << 8); } /* * data transfer stage: buffer setup */ - if (likely (len > 0)) - buf = urb->transfer_dma; - else - buf = 0; + buf = urb->transfer_dma; - /* for zero length DATA stages, STATUS is always IN */ - if (!buf || is_input) + if (is_input) token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ @@ -572,7 +572,7 @@ qh_urb_transaction ( * control requests may need a terminating data "status" ack; * bulk ones may need a terminating short packet (zero length). */ - if (likely (buf != 0)) { + if (likely (urb->transfer_buffer_length != 0)) { int one_more = 0; if (usb_pipecontrol (urb->pipe)) { diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 82f64986bc2..584b8dc6511 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -55,19 +55,13 @@ /* enqueuing/finishing log of urbs */ //#define URB_TRACE -#include <linux/config.h> #include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> #include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/smp_lock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/interrupt.h> #include <linux/usb.h> #include <linux/usb_isp116x.h> #include <linux/platform_device.h> @@ -77,14 +71,10 @@ #include <asm/system.h> #include <asm/byteorder.h> -#ifndef DEBUG -# define STUB_DEBUG_FILE -#endif - #include "../core/hcd.h" #include "isp116x.h" -#define DRIVER_VERSION "05 Aug 2005" +#define DRIVER_VERSION "03 Nov 2005" #define DRIVER_DESC "ISP116x USB Host Controller Driver" MODULE_DESCRIPTION(DRIVER_DESC); @@ -164,13 +154,11 @@ static void pack_fifo(struct isp116x *isp116x) struct ptd *ptd; int buflen = isp116x->atl_last_dir == PTD_DIR_IN ? isp116x->atl_bufshrt : isp116x->atl_buflen; - int ptd_count = 0; isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT); isp116x_write_reg16(isp116x, HCXFERCTR, buflen); isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET); for (ep = isp116x->atl_active; ep; ep = ep->active) { - ++ptd_count; ptd = &ep->ptd; dump_ptd(ptd); dump_ptd_out_data(ptd, ep->data); @@ -305,9 +293,8 @@ static void postproc_atl_queue(struct isp116x *isp116x) udev = urb->dev; ptd = &ep->ptd; cc = PTD_GET_CC(ptd); - - spin_lock(&urb->lock); short_not_ok = 1; + spin_lock(&urb->lock); /* Data underrun is special. For allowed underrun we clear the error and continue as normal. For @@ -420,7 +407,7 @@ static void postproc_atl_queue(struct isp116x *isp116x) ep->nextpid = 0; break; default: - BUG_ON(1); + BUG(); } spin_unlock(&urb->lock); } @@ -628,8 +615,12 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs) u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT); isp116x_write_reg32(isp116x, HCINTSTAT, intstat); if (intstat & HCINT_UE) { - ERR("Unrecoverable error\n"); - /* What should we do here? Reset? */ + ERR("Unrecoverable error, HC is dead!\n"); + /* IRQ's are off, we do no DMA, + perfectly ready to die ... */ + hcd->state = HC_STATE_HALT; + ret = IRQ_HANDLED; + goto done; } if (intstat & HCINT_RHSC) /* When root hub or any of its ports is going @@ -640,7 +631,6 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs) if (intstat & HCINT_RD) { DBG("---- remote wakeup\n"); usb_hcd_resume_root_hub(hcd); - ret = IRQ_HANDLED; } irqstat &= ~HCuPINT_OPR; ret = IRQ_HANDLED; @@ -651,6 +641,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs) } isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb); + done: spin_unlock(&isp116x->lock); return ret; } @@ -724,6 +715,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd, spin_lock_irqsave(&isp116x->lock, flags); if (!HC_IS_RUNNING(hcd->state)) { + kfree(ep); ret = -ENODEV; goto fail; } @@ -888,7 +880,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) { int i; - struct isp116x_ep *ep = hep->hcpriv;; + struct isp116x_ep *ep = hep->hcpriv; if (!ep) return; @@ -916,8 +908,6 @@ static int isp116x_get_frame(struct usb_hcd *hcd) return (int)fmnum; } -/*----------------------------------------------------------------*/ - /* Adapted from ohci-hub.c. Currently we don't support autosuspend. */ @@ -968,11 +958,10 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x, desc->bHubContrCurrent = 0; desc->bNbrPorts = (u8) (reg & 0x3); /* Power switching, device type, overcurrent. */ - desc->wHubCharacteristics = - (__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f)); + desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f)); desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; + desc->bitmap[0] = 0; desc->bitmap[1] = ~0; } @@ -1159,135 +1148,9 @@ static int isp116x_hub_control(struct usb_hcd *hcd, return ret; } -#ifdef CONFIG_PM - -static int isp116x_bus_suspend(struct usb_hcd *hcd) -{ - struct isp116x *isp116x = hcd_to_isp116x(hcd); - unsigned long flags; - u32 val; - int ret = 0; - - spin_lock_irqsave(&isp116x->lock, flags); - - val = isp116x_read_reg32(isp116x, HCCONTROL); - switch (val & HCCONTROL_HCFS) { - case HCCONTROL_USB_OPER: - hcd->state = HC_STATE_QUIESCING; - val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE); - val |= HCCONTROL_USB_SUSPEND; - if (hcd->remote_wakeup) - val |= HCCONTROL_RWE; - /* Wait for usb transfers to finish */ - mdelay(2); - isp116x_write_reg32(isp116x, HCCONTROL, val); - hcd->state = HC_STATE_SUSPENDED; - /* Wait for devices to suspend */ - mdelay(5); - case HCCONTROL_USB_SUSPEND: - break; - case HCCONTROL_USB_RESUME: - isp116x_write_reg32(isp116x, HCCONTROL, - (val & ~HCCONTROL_HCFS) | - HCCONTROL_USB_RESET); - case HCCONTROL_USB_RESET: - ret = -EBUSY; - break; - default: - ret = -EINVAL; - } - - spin_unlock_irqrestore(&isp116x->lock, flags); - return ret; -} - -static int isp116x_bus_resume(struct usb_hcd *hcd) -{ - struct isp116x *isp116x = hcd_to_isp116x(hcd); - u32 val; - int ret = -EINPROGRESS; - - msleep(5); - spin_lock_irq(&isp116x->lock); - - val = isp116x_read_reg32(isp116x, HCCONTROL); - switch (val & HCCONTROL_HCFS) { - case HCCONTROL_USB_SUSPEND: - val &= ~HCCONTROL_HCFS; - val |= HCCONTROL_USB_RESUME; - isp116x_write_reg32(isp116x, HCCONTROL, val); - case HCCONTROL_USB_RESUME: - break; - case HCCONTROL_USB_OPER: - /* Without setting power_state here the - SUSPENDED state won't be removed from - sysfs/usbN/power.state as a response to remote - wakeup. Maybe in the future. */ - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - ret = 0; - break; - default: - ret = -EBUSY; - } - - if (ret != -EINPROGRESS) { - spin_unlock_irq(&isp116x->lock); - return ret; - } - - val = isp116x->rhdesca & RH_A_NDP; - while (val--) { - u32 stat = - isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1); - /* force global, not selective, resume */ - if (!(stat & RH_PS_PSS)) - continue; - DBG("%s: Resuming port %d\n", __func__, val); - isp116x_write_reg32(isp116x, RH_PS_POCI, val - ? HCRHPORT2 : HCRHPORT1); - } - spin_unlock_irq(&isp116x->lock); - - hcd->state = HC_STATE_RESUMING; - mdelay(20); - - /* Go operational */ - spin_lock_irq(&isp116x->lock); - val = isp116x_read_reg32(isp116x, HCCONTROL); - isp116x_write_reg32(isp116x, HCCONTROL, - (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER); - spin_unlock_irq(&isp116x->lock); - /* see analogous comment above */ - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - hcd->state = HC_STATE_RUNNING; - - return 0; -} - - -#else - -#define isp116x_bus_suspend NULL -#define isp116x_bus_resume NULL - -#endif - /*-----------------------------------------------------------------*/ -#ifdef STUB_DEBUG_FILE - -static inline void create_debug_file(struct isp116x *isp116x) -{ -} - -static inline void remove_debug_file(struct isp116x *isp116x) -{ -} - -#else - -#include <linux/proc_fs.h> -#include <linux/seq_file.h> +#ifdef CONFIG_DEBUG_FS static void dump_irq(struct seq_file *s, char *label, u16 mask) { @@ -1311,13 +1174,9 @@ static void dump_int(struct seq_file *s, char *label, u32 mask) mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : ""); } -static int proc_isp116x_show(struct seq_file *s, void *unused) +static int isp116x_show_dbg(struct seq_file *s, void *unused) { struct isp116x *isp116x = s->private; - struct isp116x_ep *ep; - struct urb *urb; - unsigned i; - char *str; seq_printf(s, "%s\n%s version %s\n", isp116x_to_hcd(isp116x)->product_desc, hcd_name, @@ -1333,105 +1192,50 @@ static int proc_isp116x_show(struct seq_file *s, void *unused) } spin_lock_irq(&isp116x->lock); - dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB)); dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT)); dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB)); dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT)); - - list_for_each_entry(ep, &isp116x->async, schedule) { - - switch (ep->nextpid) { - case USB_PID_IN: - str = "in"; - break; - case USB_PID_OUT: - str = "out"; - break; - case USB_PID_SETUP: - str = "setup"; - break; - case USB_PID_ACK: - str = "status"; - break; - default: - str = "?"; - break; - }; - seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, - ep->epnum, str, ep->maxpacket); - list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { - seq_printf(s, " urb%p, %d/%d\n", urb, - urb->actual_length, - urb->transfer_buffer_length); - } - } - if (!list_empty(&isp116x->async)) - seq_printf(s, "\n"); - - seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); - - for (i = 0; i < PERIODIC_SIZE; i++) { - ep = isp116x->periodic[i]; - if (!ep) - continue; - seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]); - - /* DUMB: prints shared entries multiple times */ - do { - seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", - ep->period, ep, - (ep->udev->speed == - USB_SPEED_FULL) ? "" : "ls ", - ep->udev->devnum, ep->epnum, - (ep->epnum == - 0) ? "" : ((ep->nextpid == - USB_PID_IN) ? "in" : "out"), - ep->maxpacket); - ep = ep->next; - } while (ep); - } + isp116x_show_regs_seq(isp116x, s); spin_unlock_irq(&isp116x->lock); seq_printf(s, "\n"); return 0; } -static int proc_isp116x_open(struct inode *inode, struct file *file) +static int isp116x_open_seq(struct inode *inode, struct file *file) { - return single_open(file, proc_isp116x_show, PDE(inode)->data); + return single_open(file, isp116x_show_dbg, inode->u.generic_ip); } -static struct file_operations proc_ops = { - .open = proc_isp116x_open, +static struct file_operations isp116x_debug_fops = { + .open = isp116x_open_seq, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -/* expect just one isp116x per system */ -static const char proc_filename[] = "driver/isp116x"; - -static void create_debug_file(struct isp116x *isp116x) +static int create_debug_file(struct isp116x *isp116x) { - struct proc_dir_entry *pde; - - pde = create_proc_entry(proc_filename, 0, NULL); - if (pde == NULL) - return; - - pde->proc_fops = &proc_ops; - pde->data = isp116x; - isp116x->pde = pde; + isp116x->dentry = debugfs_create_file(hcd_name, + S_IRUGO, NULL, isp116x, + &isp116x_debug_fops); + if (!isp116x->dentry) + return -ENOMEM; + return 0; } static void remove_debug_file(struct isp116x *isp116x) { - if (isp116x->pde) - remove_proc_entry(proc_filename, NULL); + debugfs_remove(isp116x->dentry); } -#endif +#else + +#define create_debug_file(d) 0 +#define remove_debug_file(d) do{}while(0) + +#endif /* CONFIG_DEBUG_FS */ /*-----------------------------------------------------------------*/ @@ -1466,7 +1270,7 @@ static int isp116x_reset(struct usb_hcd *hcd) struct isp116x *isp116x = hcd_to_isp116x(hcd); unsigned long t; u16 clkrdy = 0; - int ret = 0, timeout = 15 /* ms */ ; + int ret, timeout = 15 /* ms */ ; ret = isp116x_sw_reset(isp116x); if (ret) @@ -1482,7 +1286,7 @@ static int isp116x_reset(struct usb_hcd *hcd) break; } if (!clkrdy) { - ERR("Clock not ready after 20ms\n"); + ERR("Clock not ready after %dms\n", timeout); /* After sw_reset the clock won't report to be ready, if H_WAKEUP pin is high. */ ERR("Please make sure that the H_WAKEUP pin is pulled low!\n"); @@ -1572,7 +1376,8 @@ static int isp116x_start(struct usb_hcd *hcd) val = 0; if (board->remote_wakeup_enable) { - hcd->can_wakeup = 1; + if (!device_can_wakeup(hcd->self.controller)) + device_init_wakeup(hcd->self.controller, 1); val |= RH_HS_DRWE; } isp116x_write_reg32(isp116x, HCRHSTATUS, val); @@ -1600,12 +1405,126 @@ static int isp116x_start(struct usb_hcd *hcd) isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS); isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS); - isp116x_show_regs(isp116x); + isp116x_show_regs_log(isp116x); spin_unlock_irqrestore(&isp116x->lock, flags); return 0; } -/*-----------------------------------------------------------------*/ +#ifdef CONFIG_PM + +static int isp116x_bus_suspend(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + unsigned long flags; + u32 val; + int ret = 0; + + spin_lock_irqsave(&isp116x->lock, flags); + + val = isp116x_read_reg32(isp116x, HCCONTROL); + switch (val & HCCONTROL_HCFS) { + case HCCONTROL_USB_OPER: + val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE); + val |= HCCONTROL_USB_SUSPEND; + if (device_may_wakeup(&hcd->self.root_hub->dev)) + val |= HCCONTROL_RWE; + /* Wait for usb transfers to finish */ + mdelay(2); + isp116x_write_reg32(isp116x, HCCONTROL, val); + /* Wait for devices to suspend */ + mdelay(5); + case HCCONTROL_USB_SUSPEND: + break; + case HCCONTROL_USB_RESUME: + isp116x_write_reg32(isp116x, HCCONTROL, + (val & ~HCCONTROL_HCFS) | + HCCONTROL_USB_RESET); + case HCCONTROL_USB_RESET: + ret = -EBUSY; + break; + default: + ret = -EINVAL; + } + + spin_unlock_irqrestore(&isp116x->lock, flags); + return ret; +} + +static int isp116x_bus_resume(struct usb_hcd *hcd) +{ + struct isp116x *isp116x = hcd_to_isp116x(hcd); + u32 val; + + msleep(5); + spin_lock_irq(&isp116x->lock); + + val = isp116x_read_reg32(isp116x, HCCONTROL); + switch (val & HCCONTROL_HCFS) { + case HCCONTROL_USB_SUSPEND: + val &= ~HCCONTROL_HCFS; + val |= HCCONTROL_USB_RESUME; + isp116x_write_reg32(isp116x, HCCONTROL, val); + case HCCONTROL_USB_RESUME: + break; + case HCCONTROL_USB_OPER: + spin_unlock_irq(&isp116x->lock); + /* Without setting power_state here the + SUSPENDED state won't be removed from + sysfs/usbN/power.state as a response to remote + wakeup. Maybe in the future. */ + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + return 0; + default: + /* HCCONTROL_USB_RESET: this may happen, when during + suspension the HC lost power. Reinitialize completely */ + spin_unlock_irq(&isp116x->lock); + DBG("Chip has been reset while suspended. Reinit from scratch.\n"); + isp116x_reset(hcd); + isp116x_start(hcd); + isp116x_hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_POWER, 1, NULL, 0); + if ((isp116x->rhdesca & RH_A_NDP) == 2) + isp116x_hub_control(hcd, SetPortFeature, + USB_PORT_FEAT_POWER, 2, NULL, 0); + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + return 0; + } + + val = isp116x->rhdesca & RH_A_NDP; + while (val--) { + u32 stat = + isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1); + /* force global, not selective, resume */ + if (!(stat & RH_PS_PSS)) + continue; + DBG("%s: Resuming port %d\n", __func__, val); + isp116x_write_reg32(isp116x, RH_PS_POCI, val + ? HCRHPORT2 : HCRHPORT1); + } + spin_unlock_irq(&isp116x->lock); + + hcd->state = HC_STATE_RESUMING; + msleep(20); + + /* Go operational */ + spin_lock_irq(&isp116x->lock); + val = isp116x_read_reg32(isp116x, HCCONTROL); + isp116x_write_reg32(isp116x, HCCONTROL, + (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER); + spin_unlock_irq(&isp116x->lock); + /* see analogous comment above */ + hcd->self.root_hub->dev.power.power_state = PMSG_ON; + hcd->state = HC_STATE_RUNNING; + + return 0; +} + +#else + +#define isp116x_bus_suspend NULL +#define isp116x_bus_resume NULL + +#endif static struct hc_driver isp116x_hc_driver = { .description = hcd_name, @@ -1735,12 +1654,19 @@ static int __init isp116x_probe(struct platform_device *pdev) } ret = usb_add_hcd(hcd, irq, SA_INTERRUPT); - if (ret != 0) + if (ret) goto err6; - create_debug_file(isp116x); + ret = create_debug_file(isp116x); + if (ret) { + ERR("Couldn't create debugfs entry\n"); + goto err7; + } + return 0; + err7: + usb_remove_hcd(hcd); err6: usb_put_hcd(hcd); err5: @@ -1762,13 +1688,9 @@ static int __init isp116x_probe(struct platform_device *pdev) */ static int isp116x_suspend(struct platform_device *dev, pm_message_t state) { - int ret = 0; - - VDBG("%s: state %x\n", __func__, state); - + VDBG("%s: state %x\n", __func__, state.event); dev->dev.power.power_state = state; - - return ret; + return 0; } /* @@ -1776,13 +1698,9 @@ static int isp116x_suspend(struct platform_device *dev, pm_message_t state) */ static int isp116x_resume(struct platform_device *dev) { - int ret = 0; - - VDBG("%s: state %x\n", __func__, dev->dev.power.power_state); - + VDBG("%s: state %x\n", __func__, dev->power.power_state.event); dev->dev.power.power_state = PMSG_ON; - - return ret; + return 0; } #else diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h index c6fec96785f..a1b7c3813d3 100644 --- a/drivers/usb/host/isp116x.h +++ b/drivers/usb/host/isp116x.h @@ -259,7 +259,7 @@ struct isp116x { struct isp116x_platform_data *board; - struct proc_dir_entry *pde; + struct dentry *dentry; unsigned long stat1, stat2, stat4, stat8, stat16; /* HC registers */ @@ -450,7 +450,7 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, isp116x_write_data32(isp116x, (u32) val); } -#define isp116x_show_reg(d,r) { \ +#define isp116x_show_reg_log(d,r,s) { \ if ((r) < 0x20) { \ DBG("%-12s[%02x]: %08x\n", #r, \ r, isp116x_read_reg32(d, r)); \ @@ -459,35 +459,60 @@ static void isp116x_write_reg32(struct isp116x *isp116x, unsigned reg, r, isp116x_read_reg16(d, r)); \ } \ } +#define isp116x_show_reg_seq(d,r,s) { \ + if ((r) < 0x20) { \ + seq_printf(s, "%-12s[%02x]: %08x\n", #r, \ + r, isp116x_read_reg32(d, r)); \ + } else { \ + seq_printf(s, "%-12s[%02x]: %04x\n", #r, \ + r, isp116x_read_reg16(d, r)); \ + } \ +} -static inline void isp116x_show_regs(struct isp116x *isp116x) +#define isp116x_show_regs(d,type,s) { \ + isp116x_show_reg_##type(d, HCREVISION, s); \ + isp116x_show_reg_##type(d, HCCONTROL, s); \ + isp116x_show_reg_##type(d, HCCMDSTAT, s); \ + isp116x_show_reg_##type(d, HCINTSTAT, s); \ + isp116x_show_reg_##type(d, HCINTENB, s); \ + isp116x_show_reg_##type(d, HCFMINTVL, s); \ + isp116x_show_reg_##type(d, HCFMREM, s); \ + isp116x_show_reg_##type(d, HCFMNUM, s); \ + isp116x_show_reg_##type(d, HCLSTHRESH, s); \ + isp116x_show_reg_##type(d, HCRHDESCA, s); \ + isp116x_show_reg_##type(d, HCRHDESCB, s); \ + isp116x_show_reg_##type(d, HCRHSTATUS, s); \ + isp116x_show_reg_##type(d, HCRHPORT1, s); \ + isp116x_show_reg_##type(d, HCRHPORT2, s); \ + isp116x_show_reg_##type(d, HCHWCFG, s); \ + isp116x_show_reg_##type(d, HCDMACFG, s); \ + isp116x_show_reg_##type(d, HCXFERCTR, s); \ + isp116x_show_reg_##type(d, HCuPINT, s); \ + isp116x_show_reg_##type(d, HCuPINTENB, s); \ + isp116x_show_reg_##type(d, HCCHIPID, s); \ + isp116x_show_reg_##type(d, HCSCRATCH, s); \ + isp116x_show_reg_##type(d, HCITLBUFLEN, s); \ + isp116x_show_reg_##type(d, HCATLBUFLEN, s); \ + isp116x_show_reg_##type(d, HCBUFSTAT, s); \ + isp116x_show_reg_##type(d, HCRDITL0LEN, s); \ + isp116x_show_reg_##type(d, HCRDITL1LEN, s); \ +} + +/* + Dump registers for debugfs. +*/ +static inline void isp116x_show_regs_seq(struct isp116x *isp116x, + struct seq_file *s) +{ + isp116x_show_regs(isp116x, seq, s); +} + +/* + Dump registers to syslog. +*/ +static inline void isp116x_show_regs_log(struct isp116x *isp116x) { - isp116x_show_reg(isp116x, HCREVISION); - isp116x_show_reg(isp116x, HCCONTROL); - isp116x_show_reg(isp116x, HCCMDSTAT); - isp116x_show_reg(isp116x, HCINTSTAT); - isp116x_show_reg(isp116x, HCINTENB); - isp116x_show_reg(isp116x, HCFMINTVL); - isp116x_show_reg(isp116x, HCFMREM); - isp116x_show_reg(isp116x, HCFMNUM); - isp116x_show_reg(isp116x, HCLSTHRESH); - isp116x_show_reg(isp116x, HCRHDESCA); - isp116x_show_reg(isp116x, HCRHDESCB); - isp116x_show_reg(isp116x, HCRHSTATUS); - isp116x_show_reg(isp116x, HCRHPORT1); - isp116x_show_reg(isp116x, HCRHPORT2); - isp116x_show_reg(isp116x, HCHWCFG); - isp116x_show_reg(isp116x, HCDMACFG); - isp116x_show_reg(isp116x, HCXFERCTR); - isp116x_show_reg(isp116x, HCuPINT); - isp116x_show_reg(isp116x, HCuPINTENB); - isp116x_show_reg(isp116x, HCCHIPID); - isp116x_show_reg(isp116x, HCSCRATCH); - isp116x_show_reg(isp116x, HCITLBUFLEN); - isp116x_show_reg(isp116x, HCATLBUFLEN); - isp116x_show_reg(isp116x, HCBUFSTAT); - isp116x_show_reg(isp116x, HCRDITL0LEN); - isp116x_show_reg(isp116x, HCRDITL1LEN); + isp116x_show_regs(isp116x, log, NULL); } #if defined(URB_TRACE) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index bf1d9abc07a..a4b12404ae0 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -75,13 +75,6 @@ */ #include <linux/config.h> - -#ifdef CONFIG_USB_DEBUG -# define DEBUG -#else -# undef DEBUG -#endif - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/pci.h> @@ -802,7 +795,6 @@ static int ohci_restart (struct ohci_hcd *ohci) int temp; int i; struct urb_priv *priv; - struct usb_device *root = ohci_to_hcd(ohci)->self.root_hub; /* mark any devices gone, so they do nothing till khubd disconnects. * recycle any "live" eds/tds (and urbs) right away. @@ -811,11 +803,7 @@ static int ohci_restart (struct ohci_hcd *ohci) */ spin_lock_irq(&ohci->lock); disable (ohci); - for (i = 0; i < root->maxchild; i++) { - if (root->children [i]) - usb_set_device_state (root->children[i], - USB_STATE_NOTATTACHED); - } + usb_root_hub_lost_power(ohci_to_hcd(ohci)->self.root_hub); if (!list_empty (&ohci->pending)) ohci_dbg(ohci, "abort schedule...\n"); list_for_each_entry (priv, &ohci->pending, pending) { diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 72e3b12a192..4b2226d77b3 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -372,7 +372,7 @@ done: & ohci->hc_control) == OHCI_USB_OPER && time_after (jiffies, ohci->next_statechange) - && usb_trylock_device (hcd->self.root_hub) + && usb_trylock_device (hcd->self.root_hub) == 0 ) { ohci_vdbg (ohci, "autosuspend\n"); (void) ohci_bus_suspend (hcd); diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 9d65ec30799..acde8868da2 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -26,18 +26,12 @@ #include <asm/mach-types.h> #include <asm/hardware.h> #include <asm/arch/pxa-regs.h> - - -#define PMM_NPS_MODE 1 -#define PMM_GLOBAL_MODE 2 -#define PMM_PERPORT_MODE 3 +#include <asm/arch/ohci.h> #define PXA_UHC_MAX_PORTNUM 3 #define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 ) -static int pxa27x_ohci_pmm_state; - /* PMM_NPS_MODE -- PMM Non-power switching mode Ports are powered continuously. @@ -50,8 +44,6 @@ static int pxa27x_ohci_pmm_state; */ static int pxa27x_ohci_select_pmm( int mode ) { - pxa27x_ohci_pmm_state = mode; - switch ( mode ) { case PMM_NPS_MODE: UHCRHDA |= RH_A_NPS; @@ -71,7 +63,6 @@ static int pxa27x_ohci_select_pmm( int mode ) "Invalid mode %d, set to non-power switch mode.\n", mode ); - pxa27x_ohci_pmm_state = PMM_NPS_MODE; UHCRHDA |= RH_A_NPS; } @@ -82,8 +73,13 @@ extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ -static void pxa27x_start_hc(struct platform_device *dev) +static int pxa27x_start_hc(struct device *dev) { + int retval = 0; + struct pxaohci_platform_data *inf; + + inf = dev->platform_data; + pxa_set_cken(CKEN10_USBHOST, 1); UHCHR |= UHCHR_FHR; @@ -94,21 +90,11 @@ static void pxa27x_start_hc(struct platform_device *dev) while (UHCHR & UHCHR_FSBIR) cpu_relax(); - /* This could be properly abstracted away through the - device data the day more machines are supported and - their differences can be figured out correctly. */ - if (machine_is_mainstone()) { - /* setup Port1 GPIO pin. */ - pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */ - pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */ - - /* Set the Power Control Polarity Low and Power Sense - Polarity Low to active low. Supply power to USB ports. */ - UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) & - ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE); + if (inf->init) + retval = inf->init(dev); - pxa27x_ohci_pmm_state = PMM_PERPORT_MODE; - } + if (retval < 0) + return retval; UHCHR &= ~UHCHR_SSE; @@ -117,10 +103,19 @@ static void pxa27x_start_hc(struct platform_device *dev) /* Clear any OTG Pin Hold */ if (PSSR & PSSR_OTGPH) PSSR |= PSSR_OTGPH; + + return 0; } -static void pxa27x_stop_hc(struct platform_device *dev) +static void pxa27x_stop_hc(struct device *dev) { + struct pxaohci_platform_data *inf; + + inf = dev->platform_data; + + if (inf->exit) + inf->exit(dev); + UHCHR |= UHCHR_FHR; udelay(11); UHCHR &= ~UHCHR_FHR; @@ -147,22 +142,27 @@ static void pxa27x_stop_hc(struct platform_device *dev) * through the hotplug entry's driver_data. * */ -int usb_hcd_pxa27x_probe (const struct hc_driver *driver, - struct platform_device *dev) +int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev) { int retval; struct usb_hcd *hcd; + struct pxaohci_platform_data *inf; - if (dev->resource[1].flags != IORESOURCE_IRQ) { + inf = pdev->dev.platform_data; + + if (!inf) + return -ENODEV; + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { pr_debug ("resource[1] is not IORESOURCE_IRQ"); return -ENOMEM; } - hcd = usb_create_hcd (driver, &dev->dev, "pxa27x"); + hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x"); if (!hcd) return -ENOMEM; - hcd->rsrc_start = dev->resource[0].start; - hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { pr_debug("request_mem_region failed"); @@ -177,18 +177,22 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, goto err2; } - pxa27x_start_hc(dev); + if ((retval = pxa27x_start_hc(&pdev->dev)) < 0) { + pr_debug("pxa27x_start_hc failed"); + goto err3; + } /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(pxa27x_ohci_pmm_state); + pxa27x_ohci_select_pmm(inf->port_mode); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, SA_INTERRUPT); + retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT); if (retval == 0) return retval; - pxa27x_stop_hc(dev); + pxa27x_stop_hc(&pdev->dev); + err3: iounmap(hcd->regs); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); @@ -211,10 +215,10 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, * context, normally "rmmod", "apmd", or something similar. * */ -void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *dev) +void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) { usb_remove_hcd(hcd); - pxa27x_stop_hc(dev); + pxa27x_stop_hc(&pdev->dev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); @@ -292,15 +296,12 @@ static const struct hc_driver ohci_pxa27x_hc_driver = { static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev) { - int ret; - pr_debug ("In ohci_hcd_pxa27x_drv_probe"); if (usb_disabled()) return -ENODEV; - ret = usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev); - return ret; + return usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev); } static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) @@ -308,31 +309,55 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_hcd_pxa27x_remove(hcd, pdev); + platform_set_drvdata(pdev, NULL); return 0; } -static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM +static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state) { -// struct usb_hcd *hcd = platform_get_drvdata(dev); - printk("%s: not implemented yet\n", __FUNCTION__); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + pxa27x_stop_hc(&pdev->dev); + hcd->state = HC_STATE_SUSPENDED; + pdev->dev.power.power_state = PMSG_SUSPEND; return 0; } -static int ohci_hcd_pxa27x_drv_resume(struct platform_device *dev) +static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev) { -// struct usb_hcd *hcd = platform_get_drvdata(dev); - printk("%s: not implemented yet\n", __FUNCTION__); + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int status; + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + if ((status = pxa27x_start_hc(&pdev->dev)) < 0) + return status; + + pdev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(hcd); return 0; } +#endif static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_drv_probe, .remove = ohci_hcd_pxa27x_drv_remove, +#ifdef CONFIG_PM .suspend = ohci_hcd_pxa27x_drv_suspend, .resume = ohci_hcd_pxa27x_drv_resume, +#endif .driver = { .name = "pxa27x-ohci", }, diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index e46528c825b..3ef2c0cdf1d 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -9,12 +9,6 @@ */ #include <linux/config.h> -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif - #include <linux/types.h> #include <linux/kernel.h> #include <linux/pci.h> diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index a7722a6a5a5..517360b77d8 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -32,13 +32,6 @@ #undef PACKET_TRACE #include <linux/config.h> - -#ifdef CONFIG_USB_DEBUG -# define DEBUG -#else -# undef DEBUG -#endif - #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -1581,7 +1574,9 @@ sl811h_start(struct usb_hcd *hcd) hcd->state = HC_STATE_RUNNING; if (sl811->board) { - hcd->can_wakeup = sl811->board->can_wakeup; + if (!device_can_wakeup(hcd->self.controller)) + device_init_wakeup(hcd->self.controller, + sl811->board->can_wakeup); hcd->power_budget = sl811->board->power * 2; } @@ -1805,9 +1800,10 @@ sl811h_resume(struct platform_device *dev) * let's assume it'd only be powered to enable remote wakeup. */ if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND - || !hcd->can_wakeup) { + || !device_can_wakeup(&hcd->self.root_hub->dev)) { sl811->port1 = 0; port_power(sl811, 1); + usb_root_hub_lost_power(hcd->self.root_hub); return 0; } diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index e73faf831b2..5056b745999 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -38,7 +38,7 @@ MODULE_LICENSE("GPL"); /* MACROS */ /*====================================================================*/ -#if defined(DEBUG) || defined(CONFIG_USB_DEBUG) || defined(PCMCIA_DEBUG) +#if defined(DEBUG) || defined(PCMCIA_DEBUG) static int pc_debug = 0; module_param(pc_debug, int, 0644); @@ -129,7 +129,8 @@ static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq) resources[2].end = base_addr + 1; /* The driver core will probe for us. We know sl811-hcd has been - * initialized already because of the link order dependency. + * initialized already because of the link order dependency created + * by referencing "sl811h_driver". */ platform_dev.name = sl811h_driver.name; return platform_device_register(&platform_dev); diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 151154df37f..5832953086f 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -2,8 +2,8 @@ * UHCI-specific debugging code. Invaluable when something * goes wrong, but don't get in my face. * - * Kernel visible pointers are surrounded in []'s and bus - * visible pointers are surrounded in ()'s + * Kernel visible pointers are surrounded in []s and bus + * visible pointers are surrounded in ()s * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999-2001 Johannes Erdfelt @@ -19,7 +19,7 @@ static struct dentry *uhci_debugfs_root = NULL; -/* Handle REALLY large printk's so we don't overflow buffers */ +/* Handle REALLY large printks so we don't overflow buffers */ static inline void lprintk(char *buf) { char *p; @@ -160,7 +160,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) } if (active && ni > i) { - out += sprintf(out, "%*s[skipped %d active TD's]\n", space, "", ni - i); + out += sprintf(out, "%*s[skipped %d active TDs]\n", space, "", ni - i); tmp = ntmp; td = ntd; i = ni; @@ -173,7 +173,7 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) if (list_empty(&urbp->queue_list) || urbp->queued) goto out; - out += sprintf(out, "%*sQueued QH's:\n", -space, "--"); + out += sprintf(out, "%*sQueued QHs:\n", -space, "--"); head = &urbp->queue_list; tmp = head->next; @@ -197,7 +197,7 @@ out: } #ifdef CONFIG_PROC_FS -static const char *qh_names[] = { +static const char * const qh_names[] = { "skel_int128_qh", "skel_int64_qh", "skel_int32_qh", "skel_int16_qh", "skel_int8_qh", "skel_int4_qh", @@ -464,7 +464,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) } while (tmp != head); } - out += sprintf(out, "Skeleton QH's\n"); + out += sprintf(out, "Skeleton QHs\n"); for (i = 0; i < UHCI_NUM_SKELQH; ++i) { int shown = 0; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 79efaf7d86a..dfe121d3588 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -23,11 +23,6 @@ */ #include <linux/config.h> -#ifdef CONFIG_USB_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif #include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> @@ -67,10 +62,10 @@ Alan Stern" /* * debug = 0, no debugging messages - * debug = 1, dump failed URB's except for stalls - * debug = 2, dump all failed URB's (including stalls) + * debug = 1, dump failed URBs except for stalls + * debug = 2, dump all failed URBs (including stalls) * show all queues in /debug/uhci/[pci_addr] - * debug = 3, show all TD's in URB's when dumping + * debug = 3, show all TDs in URBs when dumping */ #ifdef DEBUG static int debug = 1; @@ -93,7 +88,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); #define FSBR_DELAY msecs_to_jiffies(50) /* When we timeout an idle transfer for FSBR, we'll switch it over to */ -/* depth first traversal. We'll do it in groups of this number of TD's */ +/* depth first traversal. We'll do it in groups of this number of TDs */ /* to make sure it doesn't hog all of the bandwidth */ #define DEPTH_INTERVAL 5 @@ -478,8 +473,6 @@ static int uhci_start(struct usb_hcd *hcd) struct dentry *dentry; hcd->uses_new_polling = 1; - if (pci_find_capability(to_pci_dev(uhci_dev(uhci)), PCI_CAP_ID_PM)) - hcd->can_wakeup = 1; /* Assume it supports PME# */ dentry = debugfs_create_file(hcd->self.bus_name, S_IFREG|S_IRUGO|S_IWUSR, uhci_debugfs_root, uhci, @@ -573,7 +566,7 @@ static int uhci_start(struct usb_hcd *hcd) uhci->skel_bulk_qh->link = cpu_to_le32(uhci->skel_term_qh->dma_handle) | UHCI_PTR_QH; /* This dummy TD is to work around a bug in Intel PIIX controllers */ - uhci_fill_td(uhci->term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | + uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle); @@ -735,8 +728,9 @@ static int uhci_resume(struct usb_hcd *hcd) dev_dbg(uhci_dev(uhci), "%s\n", __FUNCTION__); - /* We aren't in D3 state anymore, we do that even if dead as I - * really don't want to keep a stale HCD_FLAG_HW_ACCESSIBLE=0 + /* Since we aren't in D3 any more, it's safe to set this flag + * even if the controller was dead. It might not even be dead + * any more, if the firmware or quirks code has reset it. */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); mb(); @@ -755,8 +749,12 @@ static int uhci_resume(struct usb_hcd *hcd) check_and_reset_hc(uhci); configure_hc(uhci); - if (uhci->rh_state == UHCI_RH_RESET) + if (uhci->rh_state == UHCI_RH_RESET) { + + /* The controller had to be reset */ + usb_root_hub_lost_power(hcd->self.root_hub); suspend_rh(uhci, UHCI_RH_SUSPENDED); + } spin_unlock_irq(&uhci->lock); @@ -882,7 +880,7 @@ static int __init uhci_hcd_init(void) init_failed: if (kmem_cache_destroy(uhci_up_cachep)) - warn("not all urb_priv's were freed!"); + warn("not all urb_privs were freed!"); up_failed: debugfs_remove(uhci_debugfs_root); @@ -900,7 +898,7 @@ static void __exit uhci_hcd_cleanup(void) pci_unregister_driver(&uhci_pci_driver); if (kmem_cache_destroy(uhci_up_cachep)) - warn("not all urb_priv's were freed!"); + warn("not all urb_privs were freed!"); debugfs_remove(uhci_debugfs_root); kfree(errbuf); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index e576db57a92..8b4b887a7d4 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -71,8 +71,6 @@ #define USBLEGSUP_RWC 0x8f00 /* the R/WC bits */ #define USBLEGSUP_RO 0x5040 /* R/O and reserved bits */ -#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ - #define UHCI_PTR_BITS cpu_to_le32(0x000F) #define UHCI_PTR_TERM cpu_to_le32(0x0001) #define UHCI_PTR_QH cpu_to_le32(0x0002) @@ -168,9 +166,11 @@ static __le32 inline qh_element(struct uhci_qh *qh) { #define TD_TOKEN_EXPLEN_MASK 0x7FF /* expected length, encoded as n - 1 */ #define TD_TOKEN_PID_MASK 0xFF -#define uhci_explen(len) ((len) << TD_TOKEN_EXPLEN_SHIFT) +#define uhci_explen(len) ((((len) - 1) & TD_TOKEN_EXPLEN_MASK) << \ + TD_TOKEN_EXPLEN_SHIFT) -#define uhci_expected_length(token) ((((token) >> 21) + 1) & TD_TOKEN_EXPLEN_MASK) +#define uhci_expected_length(token) ((((token) >> TD_TOKEN_EXPLEN_SHIFT) + \ + 1) & TD_TOKEN_EXPLEN_MASK) #define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE_SHIFT) & 1) #define uhci_endpoint(token) (((token) >> 15) & 0xf) #define uhci_devaddr(token) (((token) >> TD_TOKEN_DEVADDR_SHIFT) & 0x7f) @@ -223,10 +223,10 @@ static u32 inline td_status(struct uhci_td *td) { */ /* - * The UHCI driver places Interrupt, Control and Bulk into QH's both - * to group together TD's for one transfer, and also to faciliate queuing - * of URB's. To make it easy to insert entries into the schedule, we have - * a skeleton of QH's for each predefined Interrupt latency, low-speed + * The UHCI driver places Interrupt, Control and Bulk into QHs both + * to group together TDs for one transfer, and also to facilitate queuing + * of URBs. To make it easy to insert entries into the schedule, we have + * a skeleton of QHs for each predefined Interrupt latency, low-speed * control, full-speed control and terminating QH (see explanation for * the terminating QH below). * @@ -257,8 +257,8 @@ static u32 inline td_status(struct uhci_td *td) { * reclamation. * * Isochronous transfers are stored before the start of the skeleton - * schedule and don't use QH's. While the UHCI spec doesn't forbid the - * use of QH's for Isochronous, it doesn't use them either. And the spec + * schedule and don't use QHs. While the UHCI spec doesn't forbid the + * use of QHs for Isochronous, it doesn't use them either. And the spec * says that queues never advance on an error completion status, which * makes them totally unsuitable for Isochronous transfers. */ @@ -359,7 +359,7 @@ struct uhci_hcd { struct dma_pool *td_pool; struct uhci_td *term_td; /* Terminating TD, see UHCI bug */ - struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + struct uhci_qh *skelqh[UHCI_NUM_SKELQH]; /* Skeleton QHs */ spinlock_t lock; @@ -389,22 +389,22 @@ struct uhci_hcd { unsigned long resuming_ports; unsigned long ports_timeout; /* Time to stop signalling */ - /* Main list of URB's currently controlled by this HC */ + /* Main list of URBs currently controlled by this HC */ struct list_head urb_list; - /* List of QH's that are done, but waiting to be unlinked (race) */ + /* List of QHs that are done, but waiting to be unlinked (race) */ struct list_head qh_remove_list; unsigned int qh_remove_age; /* Age in frames */ - /* List of TD's that are done, but waiting to be freed (race) */ + /* List of TDs that are done, but waiting to be freed (race) */ struct list_head td_remove_list; unsigned int td_remove_age; /* Age in frames */ - /* List of asynchronously unlinked URB's */ + /* List of asynchronously unlinked URBs */ struct list_head urb_remove_list; unsigned int urb_remove_age; /* Age in frames */ - /* List of URB's awaiting completion callback */ + /* List of URBs awaiting completion callback */ struct list_head complete_list; int rh_numports; /* Number of root-hub ports */ diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 7e46887d9e1..b6076004a43 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -80,7 +80,7 @@ static inline void uhci_fill_td(struct uhci_td *td, u32 status, } /* - * We insert Isochronous URB's directly into the frame list at the beginning + * We insert Isochronous URBs 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) { @@ -369,7 +369,7 @@ static void uhci_append_queued_urb(struct uhci_hcd *uhci, struct urb *eurb, stru uhci_fixup_toggle(urb, uhci_toggle(td_token(lltd)) ^ 1)); - /* All qh's in the queue need to link to the next queue */ + /* All qhs in the queue need to link to the next queue */ urbp->qh->link = eurbp->qh->link; wmb(); /* Make sure we flush everything */ @@ -502,7 +502,7 @@ static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb) } /* Check to see if the remove list is empty. Set the IOC bit */ - /* to force an interrupt so we can remove the TD's*/ + /* to force an interrupt so we can remove the TDs*/ if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); @@ -596,7 +596,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(7), + uhci_fill_td(td, status, destination | uhci_explen(8), urb->setup_dma); /* @@ -612,7 +612,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur } /* - * Build the DATA TD's + * Build the DATA TDs */ while (len > 0) { int pktsze = len; @@ -628,7 +628,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1), + uhci_fill_td(td, status, destination | uhci_explen(pktsze), data); data += pktsze; @@ -658,7 +658,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, struct ur uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status | TD_CTRL_IOC, - destination | uhci_explen(UHCI_NULL_DATA_SIZE), 0); + destination | uhci_explen(0), 0); qh = uhci_alloc_qh(uhci); if (!qh) @@ -744,7 +744,7 @@ static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb) urb->actual_length = 0; - /* The rest of the TD's (but the last) are data */ + /* The rest of the TDs (but the last) are data */ tmp = tmp->next; while (tmp != head && tmp->next != head) { unsigned int ctrlstat; @@ -848,7 +848,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb status |= TD_CTRL_SPD; /* - * Build the DATA TD's + * Build the DATA TDs */ do { /* Allow zero length packets */ int pktsze = maxsze; @@ -864,7 +864,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) | + uhci_fill_td(td, status, destination | uhci_explen(pktsze) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); @@ -890,7 +890,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) | + uhci_fill_td(td, status, destination | uhci_explen(0) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); @@ -1025,7 +1025,7 @@ static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsig list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; - /* look for pending URB's with identical pipe handle */ + /* look for pending URBs with identical pipe handle */ if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS) && (u != urb)) { if (!last_urb) @@ -1092,7 +1092,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb) return -ENOMEM; uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1), + uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), urb->transfer_dma + urb->iso_frame_desc[i].offset); if (i + 1 >= urb->number_of_packets) @@ -1355,7 +1355,7 @@ static void uhci_unlink_generic(struct uhci_hcd *uhci, struct urb *urb) uhci_delete_queued_urb(uhci, urb); - /* The interrupt loop will reclaim the QH's */ + /* The interrupt loop will reclaim the QHs */ uhci_remove_qh(uhci, urbp->qh); urbp->qh = NULL; } @@ -1413,7 +1413,7 @@ static int uhci_fsbr_timeout(struct uhci_hcd *uhci, struct urb *urb) 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 + * TERM bit set) as well as we skip every so many TDs to * make sure it doesn't hog the bandwidth */ if (td->list.next != head && (count % DEPTH_INTERVAL) == diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 1d973bcf56a..049871145d6 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -962,7 +962,6 @@ MODULE_DEVICE_TABLE (usb, mdc800_table); */ static struct usb_driver mdc800_usb_driver = { - .owner = THIS_MODULE, .name = "mdc800", .probe = mdc800_usb_probe, .disconnect = mdc800_usb_disconnect, diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 950543aa5ac..458f2acdeb0 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -160,7 +160,6 @@ static void mts_usb_disconnect(struct usb_interface *intf); static struct usb_device_id mts_usb_ids []; static struct usb_driver mts_usb_driver = { - .owner = THIS_MODULE, .name = "microtekX6", .probe = mts_usb_probe, .disconnect = mts_usb_disconnect, diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 1e53934907c..509dd0a04c5 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -273,6 +273,20 @@ config USB_ATI_REMOTE To compile this driver as a module, choose M here: the module will be called ati_remote. +config USB_ATI_REMOTE2 + tristate "ATI / Philips USB RF remote control" + depends on USB && INPUT + ---help--- + Say Y here if you want to use an ATI or Philips USB RF remote control. + These are RF remotes with USB receivers. + ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards + and is also available as a separate product. + This driver provides mouse pointer, left and right mouse buttons, + and maps all the other remote buttons to keypress events. + + To compile this driver as a module, choose M here: the module will be + called ati_remote2. + config USB_KEYSPAN_REMOTE tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" depends on USB && INPUT && EXPERIMENTAL diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 07cb17db42f..d512d9f488f 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -28,6 +28,7 @@ endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o +obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_USB_HID) += usbhid.o obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c index a32558b4048..df29b8078b5 100644 --- a/drivers/usb/input/acecad.c +++ b/drivers/usb/input/acecad.c @@ -261,7 +261,6 @@ static struct usb_device_id usb_acecad_id_table [] = { MODULE_DEVICE_TABLE(usb, usb_acecad_id_table); static struct usb_driver usb_acecad_driver = { - .owner = THIS_MODULE, .name = "usb_acecad", .probe = usb_acecad_probe, .disconnect = usb_acecad_disconnect, diff --git a/drivers/usb/input/aiptek.c b/drivers/usb/input/aiptek.c index 0e2505c073d..a6693b0d1c4 100644 --- a/drivers/usb/input/aiptek.c +++ b/drivers/usb/input/aiptek.c @@ -338,7 +338,7 @@ struct aiptek { * the bitmap which comes from the tablet. This hides the * issue that the F_keys are not sequentially numbered. */ -static int macroKeyEvents[] = { +static const int macroKeyEvents[] = { KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_F13, KEY_F14, KEY_F15, KEY_F16, KEY_F17, @@ -2093,7 +2093,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) /* Programming the tablet macro keys needs to be done with a for loop * as the keycodes are discontiguous. */ - for (i = 0; i < sizeof(macroKeyEvents) / sizeof(macroKeyEvents[0]); ++i) + for (i = 0; i < ARRAY_SIZE(macroKeyEvents); ++i) set_bit(macroKeyEvents[i], inputdev->keybit); /* @@ -2135,7 +2135,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id) * not an error :-) */ - for (i = 0; i < sizeof(speeds) / sizeof(speeds[0]); ++i) { + for (i = 0; i < ARRAY_SIZE(speeds); ++i) { aiptek->curSetting.programmableDelay = speeds[i]; (void)aiptek_program_tablet(aiptek); if (aiptek->inputdev->absmax[ABS_X] > 0) { @@ -2190,7 +2190,6 @@ fail1: input_free_device(inputdev); static void aiptek_disconnect(struct usb_interface *intf); static struct usb_driver aiptek_driver = { - .owner = THIS_MODULE, .name = "aiptek", .probe = aiptek_probe, .disconnect = aiptek_disconnect, diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c index 15840db092a..1949b54f41f 100644 --- a/drivers/usb/input/appletouch.c +++ b/drivers/usb/input/appletouch.c @@ -452,7 +452,6 @@ static int atp_resume(struct usb_interface *iface) } static struct usb_driver atp_driver = { - .owner = THIS_MODULE, .name = "appletouch", .probe = atp_probe, .disconnect = atp_disconnect, diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c index 9a2a47db949..f7bdc506e61 100644 --- a/drivers/usb/input/ati_remote.c +++ b/drivers/usb/input/ati_remote.c @@ -96,6 +96,7 @@ #include <linux/usb.h> #include <linux/usb_input.h> #include <linux/wait.h> +#include <linux/jiffies.h> /* * Module and Version Information, Module Parameters @@ -146,7 +147,7 @@ static char init1[] = { 0x01, 0x00, 0x20, 0x14 }; static char init2[] = { 0x01, 0x00, 0x20, 0x14, 0x20, 0x20, 0x20 }; /* Acceleration curve for directional control pad */ -static char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; +static const char accel[] = { 1, 2, 4, 6, 9, 13, 20 }; /* Duplicate event filtering time. * Sequential, identical KIND_FILTERED inputs with less than @@ -197,7 +198,7 @@ struct ati_remote { #define KIND_ACCEL 7 /* Directional keypad - left, right, up, down.*/ /* Translation table from hardware messages to input events. */ -static struct { +static const struct { short kind; unsigned char data1, data2; int type; @@ -295,7 +296,6 @@ static void ati_remote_disconnect (struct usb_interface *interface); /* usb specific object to register with the usb subsystem */ static struct usb_driver ati_remote_driver = { - .owner = THIS_MODULE, .name = "ati_remote", .probe = ati_remote_probe, .disconnect = ati_remote_disconnect, @@ -472,7 +472,7 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) /* Filter duplicate events which happen "too close" together. */ if ((ati_remote->old_data[0] == data[1]) && (ati_remote->old_data[1] == data[2]) && - ((ati_remote->old_jiffies + FILTER_TIME) > jiffies)) { + time_before(jiffies, ati_remote->old_jiffies + FILTER_TIME)) { ati_remote->repeat_count++; } else { ati_remote->repeat_count = 0; @@ -507,16 +507,16 @@ static void ati_remote_input_report(struct urb *urb, struct pt_regs *regs) * pad down, so we increase acceleration, ramping up over two seconds to * a maximum speed. The acceleration curve is #defined above. */ - if ((jiffies - ati_remote->old_jiffies) > (HZ >> 2)) { + if (time_after(jiffies, ati_remote->old_jiffies + (HZ >> 2))) { acc = 1; ati_remote->acc_jiffies = jiffies; } - else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 3)) acc = accel[0]; - else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 2)) acc = accel[1]; - else if ((jiffies - ati_remote->acc_jiffies) < (HZ >> 1)) acc = accel[2]; - else if ((jiffies - ati_remote->acc_jiffies) < HZ ) acc = accel[3]; - else if ((jiffies - ati_remote->acc_jiffies) < HZ+(HZ>>1)) acc = accel[4]; - else if ((jiffies - ati_remote->acc_jiffies) < (HZ << 1)) acc = accel[5]; + else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 3))) acc = accel[0]; + else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 2))) acc = accel[1]; + else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ >> 1))) acc = accel[2]; + else if (time_before(jiffies, ati_remote->acc_jiffies + HZ)) acc = accel[3]; + else if (time_before(jiffies, ati_remote->acc_jiffies + HZ+(HZ>>1))) acc = accel[4]; + else if (time_before(jiffies, ati_remote->acc_jiffies + (HZ << 1))) acc = accel[5]; else acc = accel[6]; input_regs(dev, regs); diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c new file mode 100644 index 00000000000..ab1a1ae24be --- /dev/null +++ b/drivers/usb/input/ati_remote2.c @@ -0,0 +1,477 @@ +/* + * ati_remote2 - ATI/Philips USB RF remote driver + * + * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include <linux/usb_input.h> + +#define DRIVER_DESC "ATI/Philips USB RF remote driver" +#define DRIVER_VERSION "0.1" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_VERSION(DRIVER_VERSION); +MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); +MODULE_LICENSE("GPL"); + +static unsigned int mode_mask = 0x1F; +module_param(mode_mask, uint, 0644); +MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); + +static struct usb_device_id ati_remote2_id_table[] = { + { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */ + { } +}; +MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); + +static struct { + int hw_code; + int key_code; +} ati_remote2_key_table[] = { + { 0x00, KEY_0 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, + { 0x03, KEY_3 }, + { 0x04, KEY_4 }, + { 0x05, KEY_5 }, + { 0x06, KEY_6 }, + { 0x07, KEY_7 }, + { 0x08, KEY_8 }, + { 0x09, KEY_9 }, + { 0x0c, KEY_POWER }, + { 0x0d, KEY_MUTE }, + { 0x10, KEY_VOLUMEUP }, + { 0x11, KEY_VOLUMEDOWN }, + { 0x20, KEY_CHANNELUP }, + { 0x21, KEY_CHANNELDOWN }, + { 0x28, KEY_FORWARD }, + { 0x29, KEY_REWIND }, + { 0x2c, KEY_PLAY }, + { 0x30, KEY_PAUSE }, + { 0x31, KEY_STOP }, + { 0x37, KEY_RECORD }, + { 0x38, KEY_DVD }, + { 0x39, KEY_TV }, + { 0x54, KEY_MENU }, + { 0x58, KEY_UP }, + { 0x59, KEY_DOWN }, + { 0x5a, KEY_LEFT }, + { 0x5b, KEY_RIGHT }, + { 0x5c, KEY_OK }, + { 0x78, KEY_A }, + { 0x79, KEY_B }, + { 0x7a, KEY_C }, + { 0x7b, KEY_D }, + { 0x7c, KEY_E }, + { 0x7d, KEY_F }, + { 0x82, KEY_ENTER }, + { 0x8e, KEY_VENDOR }, + { 0x96, KEY_COFFEE }, + { 0xa9, BTN_LEFT }, + { 0xaa, BTN_RIGHT }, + { 0xbe, KEY_QUESTION }, + { 0xd5, KEY_FRONT }, + { 0xd0, KEY_EDIT }, + { 0xf9, KEY_INFO }, + { (0x00 << 8) | 0x3f, KEY_PROG1 }, + { (0x01 << 8) | 0x3f, KEY_PROG2 }, + { (0x02 << 8) | 0x3f, KEY_PROG3 }, + { (0x03 << 8) | 0x3f, KEY_PROG4 }, + { (0x04 << 8) | 0x3f, KEY_PC }, + { 0, KEY_RESERVED } +}; + +struct ati_remote2 { + struct input_dev *idev; + struct usb_device *udev; + + struct usb_interface *intf[2]; + struct usb_endpoint_descriptor *ep[2]; + struct urb *urb[2]; + void *buf[2]; + dma_addr_t buf_dma[2]; + + unsigned long jiffies; + int mode; + + char name[64]; + char phys[64]; +}; + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); +static void ati_remote2_disconnect(struct usb_interface *interface); + +static struct usb_driver ati_remote2_driver = { + .name = "ati_remote2", + .probe = ati_remote2_probe, + .disconnect = ati_remote2_disconnect, + .id_table = ati_remote2_id_table, +}; + +static int ati_remote2_open(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = idev->private; + int r; + + r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); + if (r) { + dev_err(&ar2->intf[0]->dev, + "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); + return r; + } + r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); + if (r) { + usb_kill_urb(ar2->urb[0]); + dev_err(&ar2->intf[1]->dev, + "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); + return r; + } + + return 0; +} + +static void ati_remote2_close(struct input_dev *idev) +{ + struct ati_remote2 *ar2 = idev->private; + + usb_kill_urb(ar2->urb[0]); + usb_kill_urb(ar2->urb[1]); +} + +static void ati_remote2_input_mouse(struct ati_remote2 *ar2, struct pt_regs *regs) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[0]; + + if (data[0] > 4) { + dev_err(&ar2->intf[0]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + if (!((1 << data[0]) & mode_mask)) + return; + + input_regs(idev, regs); + input_event(idev, EV_REL, REL_X, (s8) data[1]); + input_event(idev, EV_REL, REL_Y, (s8) data[2]); + input_sync(idev); +} + +static int ati_remote2_lookup(unsigned int hw_code) +{ + int i; + + for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) + if (ati_remote2_key_table[i].hw_code == hw_code) + return i; + + return -1; +} + +static void ati_remote2_input_key(struct ati_remote2 *ar2, struct pt_regs *regs) +{ + struct input_dev *idev = ar2->idev; + u8 *data = ar2->buf[1]; + int hw_code, index; + + if (data[0] > 4) { + dev_err(&ar2->intf[1]->dev, + "Unknown mode byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + hw_code = data[2]; + /* + * Mode keys (AUX1-AUX4, PC) all generate the same code byte. + * Use the mode byte to figure out which one was pressed. + */ + if (hw_code == 0x3f) { + /* + * For some incomprehensible reason the mouse pad generates + * events which look identical to the events from the last + * pressed mode key. Naturally we don't want to generate key + * events for the mouse pad so we filter out any subsequent + * events from the same mode key. + */ + if (ar2->mode == data[0]) + return; + + if (data[1] == 0) + ar2->mode = data[0]; + + hw_code |= data[0] << 8; + } + + if (!((1 << data[0]) & mode_mask)) + return; + + index = ati_remote2_lookup(hw_code); + if (index < 0) { + dev_err(&ar2->intf[1]->dev, + "Unknown code byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + switch (data[1]) { + case 0: /* release */ + break; + case 1: /* press */ + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]); + break; + case 2: /* repeat */ + + /* No repeat for mouse buttons. */ + if (ati_remote2_key_table[index].key_code == BTN_LEFT || + ati_remote2_key_table[index].key_code == BTN_RIGHT) + return; + + if (!time_after_eq(jiffies, ar2->jiffies)) + return; + + ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]); + break; + default: + dev_err(&ar2->intf[1]->dev, + "Unknown state byte (%02x %02x %02x %02x)\n", + data[3], data[2], data[1], data[0]); + return; + } + + input_regs(idev, regs); + input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); + input_sync(idev); +} + +static void ati_remote2_complete_mouse(struct urb *urb, struct pt_regs *regs) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + ati_remote2_input_mouse(ar2, regs); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + return; + default: + dev_err(&ar2->intf[0]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[0]->dev, + "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); +} + +static void ati_remote2_complete_key(struct urb *urb, struct pt_regs *regs) +{ + struct ati_remote2 *ar2 = urb->context; + int r; + + switch (urb->status) { + case 0: + ati_remote2_input_key(ar2, regs); + break; + case -ENOENT: + case -EILSEQ: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + return; + default: + dev_err(&ar2->intf[1]->dev, + "%s(): urb status = %d\n", __FUNCTION__, urb->status); + } + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + dev_err(&ar2->intf[1]->dev, + "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); +} + +static int ati_remote2_input_init(struct ati_remote2 *ar2) +{ + struct input_dev *idev; + int i; + + idev = input_allocate_device(); + if (!idev) + return -ENOMEM; + + ar2->idev = idev; + idev->private = ar2; + + idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); + idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); + idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); + for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) + set_bit(ati_remote2_key_table[i].key_code, idev->keybit); + + idev->rep[REP_DELAY] = 250; + idev->rep[REP_PERIOD] = 33; + + idev->open = ati_remote2_open; + idev->close = ati_remote2_close; + + idev->name = ar2->name; + idev->phys = ar2->phys; + + usb_to_input_id(ar2->udev, &idev->id); + idev->cdev.dev = &ar2->udev->dev; + + i = input_register_device(idev); + if (i) + input_free_device(idev); + + return i; +} + +static int ati_remote2_urb_init(struct ati_remote2 *ar2) +{ + struct usb_device *udev = ar2->udev; + int i, pipe, maxp; + + for (i = 0; i < 2; i++) { + ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]); + if (!ar2->buf[i]) + return -ENOMEM; + + ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ar2->urb[i]) + return -ENOMEM; + + pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + maxp = maxp > 4 ? 4 : maxp; + + usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, + i ? ati_remote2_complete_key : ati_remote2_complete_mouse, + ar2, ar2->ep[i]->bInterval); + ar2->urb[i]->transfer_dma = ar2->buf_dma[i]; + ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + return 0; +} + +static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) +{ + int i; + + for (i = 0; i < 2; i++) { + if (ar2->urb[i]) + usb_free_urb(ar2->urb[i]); + + if (ar2->buf[i]) + usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]); + } +} + +static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *alt = interface->cur_altsetting; + struct ati_remote2 *ar2; + int r; + + if (alt->desc.bInterfaceNumber) + return -ENODEV; + + ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL); + if (!ar2) + return -ENOMEM; + + ar2->udev = udev; + + ar2->intf[0] = interface; + ar2->ep[0] = &alt->endpoint[0].desc; + + ar2->intf[1] = usb_ifnum_to_if(udev, 1); + r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); + if (r) + goto fail1; + alt = ar2->intf[1]->cur_altsetting; + ar2->ep[1] = &alt->endpoint[0].desc; + + r = ati_remote2_urb_init(ar2); + if (r) + goto fail2; + + usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); + strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); + + strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); + + r = ati_remote2_input_init(ar2); + if (r) + goto fail2; + + usb_set_intfdata(interface, ar2); + + return 0; + + fail2: + ati_remote2_urb_cleanup(ar2); + + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + fail1: + kfree(ar2); + + return r; +} + +static void ati_remote2_disconnect(struct usb_interface *interface) +{ + struct ati_remote2 *ar2; + struct usb_host_interface *alt = interface->cur_altsetting; + + if (alt->desc.bInterfaceNumber) + return; + + ar2 = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + input_unregister_device(ar2->idev); + + ati_remote2_urb_cleanup(ar2); + + usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); + + kfree(ar2); +} + +static int __init ati_remote2_init(void) +{ + int r; + + r = usb_register(&ati_remote2_driver); + if (r) + printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r); + else + printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n"); + + return r; +} + +static void __exit ati_remote2_exit(void) +{ + usb_deregister(&ati_remote2_driver); +} + +module_init(ati_remote2_init); +module_exit(ati_remote2_exit); diff --git a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h index 26ca5b890a6..b44d398de07 100644 --- a/drivers/usb/input/fixp-arith.h +++ b/drivers/usb/input/fixp-arith.h @@ -38,7 +38,7 @@ typedef s16 fixp_t; #define FRAC_MASK ((1<<FRAC_N)-1) // Not to be used directly. Use fixp_{cos,sin} -static fixp_t cos_table[45] = { +static const fixp_t cos_table[45] = { 0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD, 0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1, diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c index a3e44ef1df4..5f52979af1c 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/usb/input/hid-core.c @@ -1454,7 +1454,7 @@ void hid_init_reports(struct hid_device *hid) * Alphabetically sorted blacklist by quirk type. */ -static struct hid_blacklist { +static const struct hid_blacklist { __u16 idVendor; __u16 idProduct; unsigned quirks; @@ -1930,7 +1930,6 @@ static struct usb_device_id hid_usb_ids [] = { MODULE_DEVICE_TABLE (usb, hid_usb_ids); static struct usb_driver hid_driver = { - .owner = THIS_MODULE, .name = "usbhid", .probe = hid_probe, .disconnect = hid_disconnect, diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 1220a5004a5..192a03b2897 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -39,7 +39,7 @@ #define unk KEY_UNKNOWN -static unsigned char hid_keyboard[256] = { +static const unsigned char hid_keyboard[256] = { 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, @@ -58,7 +58,7 @@ static unsigned char hid_keyboard[256] = { 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk }; -static struct { +static const struct { __s32 x; __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c index 440377c7a0d..4dff8473553 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/usb/input/hiddev.c @@ -826,7 +826,6 @@ static int hiddev_usbd_probe(struct usb_interface *intf, static /* const */ struct usb_driver hiddev_driver = { - .owner = THIS_MODULE, .name = "hiddev", .probe = hiddev_usbd_probe, }; diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c index 4a50acb39d2..7618ae5c104 100644 --- a/drivers/usb/input/itmtouch.c +++ b/drivers/usb/input/itmtouch.c @@ -250,7 +250,6 @@ static void itmtouch_disconnect(struct usb_interface *intf) MODULE_DEVICE_TABLE(usb, itmtouch_ids); static struct usb_driver itmtouch_driver = { - .owner = THIS_MODULE, .name = "itmtouch", .probe = itmtouch_probe, .disconnect = itmtouch_disconnect, diff --git a/drivers/usb/input/kbtab.c b/drivers/usb/input/kbtab.c index fd48e74e78e..f6d5cead542 100644 --- a/drivers/usb/input/kbtab.c +++ b/drivers/usb/input/kbtab.c @@ -197,7 +197,6 @@ static void kbtab_disconnect(struct usb_interface *intf) } static struct usb_driver kbtab_driver = { - .owner = THIS_MODULE, .name = "kbtab", .probe = kbtab_probe, .disconnect = kbtab_disconnect, diff --git a/drivers/usb/input/keyspan_remote.c b/drivers/usb/input/keyspan_remote.c index a32cfe51b77..b4a051b549d 100644 --- a/drivers/usb/input/keyspan_remote.c +++ b/drivers/usb/input/keyspan_remote.c @@ -95,7 +95,7 @@ struct usb_keyspan { * Currently there are 15 and 17 button models so RESERVED codes * are blank areas in the mapping. */ -static int keyspan_key_table[] = { +static const int keyspan_key_table[] = { KEY_RESERVED, /* 0 is just a place holder. */ KEY_RESERVED, KEY_STOP, @@ -559,7 +559,6 @@ static void keyspan_disconnect(struct usb_interface *interface) */ static struct usb_driver keyspan_driver = { - .owner = THIS_MODULE, .name = "keyspan_remote", .probe = keyspan_probe, .disconnect = keyspan_disconnect, diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c index 52cc18cd247..f018953a548 100644 --- a/drivers/usb/input/mtouchusb.c +++ b/drivers/usb/input/mtouchusb.c @@ -310,7 +310,6 @@ static void mtouchusb_disconnect(struct usb_interface *intf) MODULE_DEVICE_TABLE(usb, mtouchusb_devices); static struct usb_driver mtouchusb_driver = { - .owner = THIS_MODULE, .name = "mtouchusb", .probe = mtouchusb_probe, .disconnect = mtouchusb_disconnect, diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c index b7476233ef5..fdf0f788062 100644 --- a/drivers/usb/input/powermate.c +++ b/drivers/usb/input/powermate.c @@ -441,7 +441,6 @@ static struct usb_device_id powermate_devices [] = { MODULE_DEVICE_TABLE (usb, powermate_devices); static struct usb_driver powermate_driver = { - .owner = THIS_MODULE, .name = "powermate", .probe = powermate_probe, .disconnect = powermate_disconnect, diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c index 7420c6b8428..3b3c7b4120a 100644 --- a/drivers/usb/input/touchkitusb.c +++ b/drivers/usb/input/touchkitusb.c @@ -1,7 +1,7 @@ /****************************************************************************** * touchkitusb.c -- Driver for eGalax TouchKit USB Touchscreens * - * Copyright (C) 2004 by Daniel Ritz + * Copyright (C) 2004-2005 by Daniel Ritz <daniel.ritz@gmx.ch> * Copyright (C) by Todd E. Johnson (mtouchusb.c) * * This program is free software; you can redistribute it and/or @@ -41,15 +41,13 @@ #define TOUCHKIT_MAX_YC 0x07ff #define TOUCHKIT_YC_FUZZ 0x0 #define TOUCHKIT_YC_FLAT 0x0 -#define TOUCHKIT_REPORT_DATA_SIZE 8 +#define TOUCHKIT_REPORT_DATA_SIZE 16 #define TOUCHKIT_DOWN 0x01 -#define TOUCHKIT_POINT_TOUCH 0x81 -#define TOUCHKIT_POINT_NOTOUCH 0x80 -#define TOUCHKIT_GET_TOUCHED(dat) ((((dat)[0]) & TOUCHKIT_DOWN) ? 1 : 0) -#define TOUCHKIT_GET_X(dat) (((dat)[3] << 7) | (dat)[4]) -#define TOUCHKIT_GET_Y(dat) (((dat)[1] << 7) | (dat)[2]) +#define TOUCHKIT_PKT_TYPE_MASK 0xFE +#define TOUCHKIT_PKT_TYPE_REPT 0x80 +#define TOUCHKIT_PKT_TYPE_DIAG 0x0A #define DRIVER_VERSION "v0.1" #define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>" @@ -62,6 +60,8 @@ MODULE_PARM_DESC(swap_xy, "If set X and Y axes are swapped."); struct touchkit_usb { unsigned char *data; dma_addr_t data_dma; + char buffer[TOUCHKIT_REPORT_DATA_SIZE]; + int buf_len; struct urb *irq; struct usb_device *udev; struct input_dev *input; @@ -77,11 +77,128 @@ static struct usb_device_id touchkit_devices[] = { {} }; +/* helpers to read the data */ +static inline int touchkit_get_touched(char *data) +{ + return (data[0] & TOUCHKIT_DOWN) ? 1 : 0; +} + +static inline int touchkit_get_x(char *data) +{ + return ((data[3] & 0x0F) << 7) | (data[4] & 0x7F); +} + +static inline int touchkit_get_y(char *data) +{ + return ((data[1] & 0x0F) << 7) | (data[2] & 0x7F); +} + + +/* processes one input packet. */ +static void touchkit_process_pkt(struct touchkit_usb *touchkit, + struct pt_regs *regs, char *pkt) +{ + int x, y; + + /* only process report packets */ + if ((pkt[0] & TOUCHKIT_PKT_TYPE_MASK) != TOUCHKIT_PKT_TYPE_REPT) + return; + + if (swap_xy) { + y = touchkit_get_x(pkt); + x = touchkit_get_y(pkt); + } else { + x = touchkit_get_x(pkt); + y = touchkit_get_y(pkt); + } + + input_regs(touchkit->input, regs); + input_report_key(touchkit->input, BTN_TOUCH, touchkit_get_touched(pkt)); + input_report_abs(touchkit->input, ABS_X, x); + input_report_abs(touchkit->input, ABS_Y, y); + input_sync(touchkit->input); +} + + +static int touchkit_get_pkt_len(char *buf) +{ + switch (buf[0] & TOUCHKIT_PKT_TYPE_MASK) { + case TOUCHKIT_PKT_TYPE_REPT: + return 5; + + case TOUCHKIT_PKT_TYPE_DIAG: + return buf[1] + 2; + } + + return 0; +} + +static void touchkit_process(struct touchkit_usb *touchkit, int len, + struct pt_regs *regs) +{ + char *buffer; + int pkt_len, buf_len, pos; + + /* if the buffer contains data, append */ + if (unlikely(touchkit->buf_len)) { + int tmp; + + /* if only 1 byte in buffer, add another one to get length */ + if (touchkit->buf_len == 1) + touchkit->buffer[1] = touchkit->data[0]; + + pkt_len = touchkit_get_pkt_len(touchkit->buffer); + + /* unknown packet: drop everything */ + if (!pkt_len) + return; + + /* append, process */ + tmp = pkt_len - touchkit->buf_len; + memcpy(touchkit->buffer + touchkit->buf_len, touchkit->data, tmp); + touchkit_process_pkt(touchkit, regs, touchkit->buffer); + + buffer = touchkit->data + tmp; + buf_len = len - tmp; + } else { + buffer = touchkit->data; + buf_len = len; + } + + /* only one byte left in buffer */ + if (unlikely(buf_len == 1)) { + touchkit->buffer[0] = buffer[0]; + touchkit->buf_len = 1; + return; + } + + /* loop over the buffer */ + pos = 0; + while (pos < buf_len) { + /* get packet len */ + pkt_len = touchkit_get_pkt_len(buffer + pos); + + /* unknown packet: drop everything */ + if (unlikely(!pkt_len)) + return; + + /* full packet: process */ + if (likely(pkt_len <= buf_len)) { + touchkit_process_pkt(touchkit, regs, buffer + pos); + } else { + /* incomplete packet: save in buffer */ + memcpy(touchkit->buffer, buffer + pos, buf_len - pos); + touchkit->buf_len = buf_len - pos; + } + pos += pkt_len; + } +} + + static void touchkit_irq(struct urb *urb, struct pt_regs *regs) { struct touchkit_usb *touchkit = urb->context; int retval; - int x, y; switch (urb->status) { case 0: @@ -105,20 +222,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs) goto exit; } - if (swap_xy) { - y = TOUCHKIT_GET_X(touchkit->data); - x = TOUCHKIT_GET_Y(touchkit->data); - } else { - x = TOUCHKIT_GET_X(touchkit->data); - y = TOUCHKIT_GET_Y(touchkit->data); - } - - input_regs(touchkit->input, regs); - input_report_key(touchkit->input, BTN_TOUCH, - TOUCHKIT_GET_TOUCHED(touchkit->data)); - input_report_abs(touchkit->input, ABS_X, x); - input_report_abs(touchkit->input, ABS_Y, y); - input_sync(touchkit->input); + touchkit_process(touchkit, urb->actual_length, regs); exit: retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -267,7 +371,6 @@ static void touchkit_disconnect(struct usb_interface *intf) MODULE_DEVICE_TABLE(usb, touchkit_devices); static struct usb_driver touchkit_driver = { - .owner = THIS_MODULE, .name = "touchkitusb", .probe = touchkit_probe, .disconnect = touchkit_disconnect, diff --git a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c index 226b6f90a90..2f3edc26cb5 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/usb/input/usbkbd.c @@ -345,7 +345,6 @@ static struct usb_device_id usb_kbd_id_table [] = { MODULE_DEVICE_TABLE (usb, usb_kbd_id_table); static struct usb_driver usb_kbd_driver = { - .owner = THIS_MODULE, .name = "usbkbd", .probe = usb_kbd_probe, .disconnect = usb_kbd_disconnect, diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c index 230f6b1b314..af526135d21 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/usb/input/usbmouse.c @@ -226,7 +226,6 @@ static struct usb_device_id usb_mouse_id_table [] = { MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); static struct usb_driver usb_mouse_driver = { - .owner = THIS_MODULE, .name = "usbmouse", .probe = usb_mouse_probe, .disconnect = usb_mouse_disconnect, diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c index dc099bbe12b..48df4cfd5a4 100644 --- a/drivers/usb/input/wacom.c +++ b/drivers/usb/input/wacom.c @@ -945,7 +945,6 @@ static void wacom_disconnect(struct usb_interface *intf) } static struct usb_driver wacom_driver = { - .owner = THIS_MODULE, .name = "wacom", .probe = wacom_probe, .disconnect = wacom_disconnect, diff --git a/drivers/usb/input/xpad.c b/drivers/usb/input/xpad.c index 43112f040b6..e278489a80c 100644 --- a/drivers/usb/input/xpad.c +++ b/drivers/usb/input/xpad.c @@ -70,7 +70,7 @@ #define XPAD_PKT_LEN 32 -static struct xpad_device { +static const struct xpad_device { u16 idVendor; u16 idProduct; char *name; @@ -81,13 +81,13 @@ static struct xpad_device { { 0x0000, 0x0000, "X-Box pad" } }; -static signed short xpad_btn[] = { +static const signed short xpad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, /* "analog" buttons */ BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ -1 /* terminating entry */ }; -static signed short xpad_abs[] = { +static const signed short xpad_abs[] = { ABS_X, ABS_Y, /* left stick */ ABS_RX, ABS_RY, /* right stick */ ABS_Z, ABS_RZ, /* triggers left/right */ @@ -316,7 +316,6 @@ static void xpad_disconnect(struct usb_interface *intf) } static struct usb_driver xpad_driver = { - .owner = THIS_MODULE, .name = "xpad", .probe = xpad_probe, .disconnect = xpad_disconnect, diff --git a/drivers/usb/input/yealink.c b/drivers/usb/input/yealink.c index f526aebea50..1bfc105ad4d 100644 --- a/drivers/usb/input/yealink.c +++ b/drivers/usb/input/yealink.c @@ -987,7 +987,6 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id) } static struct usb_driver yealink_driver = { - .owner = THIS_MODULE, .name = "yealink", .probe = usb_probe, .disconnect = usb_disconnect, diff --git a/drivers/usb/media/dabusb.c b/drivers/usb/media/dabusb.c index 27b23c55bbc..18d8eaf408d 100644 --- a/drivers/usb/media/dabusb.c +++ b/drivers/usb/media/dabusb.c @@ -812,7 +812,6 @@ static struct usb_device_id dabusb_ids [] = { MODULE_DEVICE_TABLE (usb, dabusb_ids); static struct usb_driver dabusb_driver = { - .owner = THIS_MODULE, .name = "dabusb", .probe = dabusb_probe, .disconnect = dabusb_disconnect, diff --git a/drivers/usb/media/dsbr100.c b/drivers/usb/media/dsbr100.c index 7503f5b96f5..6a5700e9d42 100644 --- a/drivers/usb/media/dsbr100.c +++ b/drivers/usb/media/dsbr100.c @@ -150,7 +150,6 @@ MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); /* USB subsystem interface */ static struct usb_driver usb_dsbr100_driver = { - .owner = THIS_MODULE, .name = "dsbr100", .probe = usb_dsbr100_probe, .disconnect = usb_dsbr100_disconnect, diff --git a/drivers/usb/media/ibmcam.c b/drivers/usb/media/ibmcam.c index ba41fc7b95c..a42c2229412 100644 --- a/drivers/usb/media/ibmcam.c +++ b/drivers/usb/media/ibmcam.c @@ -3457,7 +3457,7 @@ static void ibmcam_model3_setup_after_video_if(struct uvd *uvd) if(init_model3_input) { if (debug > 0) info("Setting input to RCA."); - for (i=0; i < (sizeof(initData)/sizeof(initData[0])); i++) { + for (i=0; i < ARRAY_SIZE(initData); i++) { ibmcam_veio(uvd, initData[i].req, initData[i].value, initData[i].index); } } diff --git a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c index 9fe2c2710d1..e2ede583518 100644 --- a/drivers/usb/media/konicawc.c +++ b/drivers/usb/media/konicawc.c @@ -77,14 +77,14 @@ static int saturation = MAX_SATURATION/2; static int sharpness = MAX_SHARPNESS/2; static int whitebal = 3*(MAX_WHITEBAL/4); -static int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 }; +static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 }; /* These FPS speeds are from the windows config box. They are * indexed on size (0-2) and speed (0-6). Divide by 3 to get the * real fps. */ -static int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 }, +static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 }, { 24, 40, 48, 60, 72, 80, 100 }, { 18, 30, 36, 45, 54, 60, 75 }, { 6, 10, 12, 15, 18, 21, 25 } }; @@ -95,7 +95,7 @@ struct cam_size { u8 cmd; }; -static struct cam_size camera_sizes[] = { { 160, 120, 0x7 }, +static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 }, { 160, 136, 0xa }, { 176, 144, 0x4 }, { 320, 240, 0x5 } }; diff --git a/drivers/usb/media/ov511.c b/drivers/usb/media/ov511.c index 036c485d1d1..3a0e8ce67eb 100644 --- a/drivers/usb/media/ov511.c +++ b/drivers/usb/media/ov511.c @@ -211,7 +211,7 @@ static struct ov51x_decomp_ops *ov518_mmx_decomp_ops; /* Number of times to retry a failed I2C transaction. Increase this if you * are getting "Failed to read sensor ID..." */ -static int i2c_detect_tries = 5; +static const int i2c_detect_tries = 5; /* MMX support is present in kernel and CPU. Checked upon decomp module load. */ #if defined(__i386__) || defined(__x86_64__) @@ -6008,7 +6008,6 @@ ov51x_disconnect(struct usb_interface *intf) } static struct usb_driver ov511_driver = { - .owner = THIS_MODULE, .name = "ov511", .id_table = device_table, .probe = ov51x_probe, diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c index 53099190952..359c4b2df73 100644 --- a/drivers/usb/media/pwc/pwc-ctrl.c +++ b/drivers/usb/media/pwc/pwc-ctrl.c @@ -109,7 +109,7 @@ #define PT_RESET_CONTROL_FORMATTER 0x02 #define PT_STATUS_FORMATTER 0x03 -static char *size2name[PSZ_MAX] = +static const char *size2name[PSZ_MAX] = { "subQCIF", "QSIF", diff --git a/drivers/usb/media/pwc/pwc-if.c b/drivers/usb/media/pwc/pwc-if.c index 5524fd70210..09ca6128ac2 100644 --- a/drivers/usb/media/pwc/pwc-if.c +++ b/drivers/usb/media/pwc/pwc-if.c @@ -111,7 +111,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id static void usb_pwc_disconnect(struct usb_interface *intf); static struct usb_driver pwc_driver = { - .owner = THIS_MODULE, .name = "Philips webcam", /* name */ .id_table = pwc_device_table, .probe = usb_pwc_probe, /* probe() */ diff --git a/drivers/usb/media/se401.c b/drivers/usb/media/se401.c index f69e443cd1b..b2ae29af594 100644 --- a/drivers/usb/media/se401.c +++ b/drivers/usb/media/se401.c @@ -1401,7 +1401,6 @@ static void se401_disconnect(struct usb_interface *intf) } static struct usb_driver se401_driver = { - .owner = THIS_MODULE, .name = "se401", .id_table = device_table, .probe = se401_probe, diff --git a/drivers/usb/media/sn9c102_core.c b/drivers/usb/media/sn9c102_core.c index b2e66e3b90a..8d1a1c357d5 100644 --- a/drivers/usb/media/sn9c102_core.c +++ b/drivers/usb/media/sn9c102_core.c @@ -1316,7 +1316,7 @@ static int sn9c102_init(struct sn9c102_device* cam) struct v4l2_control ctrl; struct v4l2_queryctrl *qctrl; struct v4l2_rect* rect; - u8 i = 0, n = 0; + u8 i = 0; int err = 0; if (!(cam->state & DEV_INITIALIZED)) { @@ -1352,7 +1352,7 @@ static int sn9c102_init(struct sn9c102_device* cam) return err; if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - DBG(3, "Compressed video format is active, quality %d", + DBG(3, "Compressed video format is active, quality %d", cam->compression.quality) else DBG(3, "Uncompressed video format is active") @@ -1364,9 +1364,8 @@ static int sn9c102_init(struct sn9c102_device* cam) } if (s->set_ctrl) { - n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); - for (i = 0; i < n; i++) - if (s->qctrl[i].id != 0 && + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) + if (s->qctrl[i].id != 0 && !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl.id = s->qctrl[i].id; ctrl.value = qctrl[i].default_value; @@ -1388,7 +1387,7 @@ static int sn9c102_init(struct sn9c102_device* cam) init_waitqueue_head(&cam->wait_stream); cam->nreadbuffers = 2; memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), + memcpy(&(s->_rect), &(s->cropcap.defrect), sizeof(struct v4l2_rect)); cam->state |= DEV_INITIALIZED; } @@ -1810,13 +1809,12 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, { struct sn9c102_sensor* s = cam->sensor; struct v4l2_queryctrl qc; - u8 i, n; + u8 i; if (copy_from_user(&qc, arg, sizeof(qc))) return -EFAULT; - n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); - for (i = 0; i < n; i++) + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) if (qc.id && qc.id == s->qctrl[i].id) { memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); if (copy_to_user(arg, &qc, sizeof(qc))) @@ -1852,7 +1850,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, { struct sn9c102_sensor* s = cam->sensor; struct v4l2_control ctrl; - u8 i, n; + u8 i; int err = 0; if (!s->set_ctrl) @@ -1861,8 +1859,7 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - n = sizeof(s->qctrl) / sizeof(s->qctrl[0]); - for (i = 0; i < n; i++) + for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) if (ctrl.id == s->qctrl[i].id) { if (ctrl.value < s->qctrl[i].minimum || ctrl.value > s->qctrl[i].maximum) @@ -2544,7 +2541,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) unsigned int i, n; int err = 0, r; - n = sizeof(sn9c102_id_table)/sizeof(sn9c102_id_table[0]); + n = ARRAY_SIZE(sn9c102_id_table); for (i = 0; i < n-1; i++) if (le16_to_cpu(udev->descriptor.idVendor) == sn9c102_id_table[i].idVendor && @@ -2711,7 +2708,6 @@ static void sn9c102_usb_disconnect(struct usb_interface* intf) static struct usb_driver sn9c102_usb_driver = { - .owner = THIS_MODULE, .name = "sn9c102", .id_table = sn9c102_id_table, .probe = sn9c102_usb_probe, diff --git a/drivers/usb/media/stv680.c b/drivers/usb/media/stv680.c index 0fd0fa9fec2..774038b352c 100644 --- a/drivers/usb/media/stv680.c +++ b/drivers/usb/media/stv680.c @@ -1477,7 +1477,6 @@ static void stv680_disconnect (struct usb_interface *intf) } static struct usb_driver stv680_driver = { - .owner = THIS_MODULE, .name = "stv680", .probe = stv680_probe, .disconnect = stv680_disconnect, diff --git a/drivers/usb/media/stv680.h b/drivers/usb/media/stv680.h index 44594061260..b0551cdb280 100644 --- a/drivers/usb/media/stv680.h +++ b/drivers/usb/media/stv680.h @@ -151,7 +151,7 @@ struct usb_stv { }; -static unsigned char red[256] = { +static const unsigned char red[256] = { 0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42, 44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69, @@ -176,7 +176,7 @@ static unsigned char red[256] = { 220, 220, 221, 221 }; -static unsigned char green[256] = { +static const unsigned char green[256] = { 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47, 50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77, @@ -201,7 +201,7 @@ static unsigned char green[256] = { 245, 245, 246, 246 }; -static unsigned char blue[256] = { +static const unsigned char blue[256] = { 0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51, 55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84, diff --git a/drivers/usb/media/usbvideo.c b/drivers/usb/media/usbvideo.c index 24efb21969c..4bd113325ef 100644 --- a/drivers/usb/media/usbvideo.c +++ b/drivers/usb/media/usbvideo.c @@ -725,7 +725,7 @@ int usbvideo_register( /* Allocate user_data separately because of kmalloc's limits */ if (num_extra > 0) { up->user_size = num_cams * num_extra; - up->user_data = (char *) kmalloc(up->user_size, GFP_KERNEL); + up->user_data = kmalloc(up->user_size, GFP_KERNEL); if (up->user_data == NULL) { err("%s: Failed to allocate user_data (%d. bytes)", __FUNCTION__, up->user_size); @@ -955,7 +955,7 @@ static struct file_operations usbvideo_fops = { .ioctl = usbvideo_v4l_ioctl, .llseek = no_llseek, }; -static struct video_device usbvideo_template = { +static const struct video_device usbvideo_template = { .owner = THIS_MODULE, .type = VID_TYPE_CAPTURE, .hardware = VID_HARDWARE_CPIA, diff --git a/drivers/usb/media/vicam.c b/drivers/usb/media/vicam.c index 0bc0b1247a6..1c73155c8d7 100644 --- a/drivers/usb/media/vicam.c +++ b/drivers/usb/media/vicam.c @@ -1257,7 +1257,6 @@ static struct usb_device_id vicam_table[] = { MODULE_DEVICE_TABLE(usb, vicam_table); static struct usb_driver vicam_driver = { - .owner = THIS_MODULE, .name = "vicam", .probe = vicam_probe, .disconnect = vicam_disconnect, diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c index 67612c81cb9..04d69339c05 100644 --- a/drivers/usb/media/w9968cf.c +++ b/drivers/usb/media/w9968cf.c @@ -2958,7 +2958,7 @@ static int w9968cf_v4l_ioctl(struct inode* inode, struct file* filp, }; #define V4L1_IOCTL(cmd) \ - ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \ + ((_IOC_NR((cmd)) < ARRAY_SIZE(v4l1_ioctls)) ? \ v4l1_ioctls[_IOC_NR((cmd))] : "?") cam = (struct w9968cf_device*)video_get_drvdata(video_devdata(filp)); @@ -3554,7 +3554,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) /* Allocate 2 bytes of memory for camera control USB transfers */ - if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) { + if (!(cam->control_buffer = kmalloc(2, GFP_KERNEL))) { DBG(1,"Couldn't allocate memory for camera control transfers") err = -ENOMEM; goto fail; @@ -3562,7 +3562,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) memset(cam->control_buffer, 0, 2); /* Allocate 8 bytes of memory for USB data transfers to the FSB */ - if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) { + if (!(cam->data_buffer = kmalloc(8, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for data " "transfers to the FSB") err = -ENOMEM; @@ -3668,7 +3668,6 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) static struct usb_driver w9968cf_usb_driver = { - .owner = THIS_MODULE, .name = "w9968cf", .id_table = winbond_id_table, .probe = w9968cf_usb_probe, diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c index b293db3c28c..449b2501acf 100644 --- a/drivers/usb/misc/auerswald.c +++ b/drivers/usb/misc/auerswald.c @@ -767,7 +767,7 @@ static int auerbuf_setup (pauerbufctl_t bcp, unsigned int numElements, unsigned memset (bep, 0, sizeof (auerbuf_t)); bep->list = bcp; INIT_LIST_HEAD (&bep->buff_list); - bep->bufp = (char *) kmalloc (bufsize, GFP_KERNEL); + bep->bufp = kmalloc (bufsize, GFP_KERNEL); if (!bep->bufp) goto bl_fail; bep->dr = (struct usb_ctrlrequest *) kmalloc (sizeof (struct usb_ctrlrequest), GFP_KERNEL); @@ -1123,7 +1123,7 @@ static int auerswald_int_open (pauerswald_t cp) } } if (!cp->intbufp) { - cp->intbufp = (char *) kmalloc (irqsize, GFP_KERNEL); + cp->intbufp = kmalloc (irqsize, GFP_KERNEL); if (!cp->intbufp) { ret = -ENOMEM; goto intoend; @@ -2103,7 +2103,6 @@ MODULE_DEVICE_TABLE (usb, auerswald_ids); /* Standard usb driver struct */ static struct usb_driver auerswald_driver = { - .owner = THIS_MODULE, .name = "auerswald", .probe = auerswald_probe, .disconnect = auerswald_disconnect, diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index b33044d56a1..6671317b495 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -50,7 +50,6 @@ static void cytherm_disconnect(struct usb_interface *interface); /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver cytherm_driver = { - .owner = THIS_MODULE, .name = "cytherm", .probe = cytherm_probe, .disconnect = cytherm_disconnect, diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index c8155209bf4..3824df33094 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -227,7 +227,6 @@ static void emi26_disconnect(struct usb_interface *intf) } static struct usb_driver emi26_driver = { - .owner = THIS_MODULE, .name = "emi26 - firmware loader", .probe = emi26_probe, .disconnect = emi26_disconnect, diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 189986af2ac..52fea2e08db 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -266,7 +266,6 @@ static void emi62_disconnect(struct usb_interface *intf) } static struct usb_driver emi62_driver = { - .owner = THIS_MODULE, .name = "emi62 - firmware loader", .probe = emi62_probe, .disconnect = emi62_disconnect, diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 1dc3e0f7301..d8cde101798 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -114,7 +114,6 @@ static struct usb_class_driver idmouse_class = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver idmouse_driver = { - .owner = THIS_MODULE, .name = DRIVER_SHORT, .probe = idmouse_probe, .disconnect = idmouse_disconnect, diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 7e93ac96490..981d8a5fbfd 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -763,7 +763,6 @@ static void ld_usb_disconnect(struct usb_interface *intf) /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver ld_usb_driver = { - .owner = THIS_MODULE, .name = "ldusb", .probe = ld_usb_probe, .disconnect = ld_usb_disconnect, diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 2703e205bc8..1336745b8f5 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -282,7 +282,6 @@ static struct usb_class_driver tower_class = { /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver tower_driver = { - .owner = THIS_MODULE, .name = "legousbtower", .probe = tower_probe, .disconnect = tower_disconnect, diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c index 067a8148692..605a3c87e05 100644 --- a/drivers/usb/misc/phidgetkit.c +++ b/drivers/usb/misc/phidgetkit.c @@ -555,7 +555,6 @@ static void interfacekit_disconnect(struct usb_interface *interface) } static struct usb_driver interfacekit_driver = { - .owner = THIS_MODULE, .name = "phidgetkit", .probe = interfacekit_probe, .disconnect = interfacekit_disconnect, diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c index a30d4a6ee82..b3418d2bcc6 100644 --- a/drivers/usb/misc/phidgetservo.c +++ b/drivers/usb/misc/phidgetservo.c @@ -306,7 +306,6 @@ servo_disconnect(struct usb_interface *interface) } static struct usb_driver servo_driver = { - .owner = THIS_MODULE, .name = "phidgetservo", .probe = servo_probe, .disconnect = servo_disconnect, diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index 9590dbac5d9..384fa376980 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -465,14 +465,14 @@ static int probe_rio(struct usb_interface *intf, rio->rio_dev = dev; - if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) { + if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) { err("probe_rio: Not enough memory for the output buffer"); usb_deregister_dev(intf, &usb_rio_class); return -ENOMEM; } dbg("probe_rio: obuf address:%p", rio->obuf); - if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) { + if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) { err("probe_rio: Not enough memory for the input buffer"); usb_deregister_dev(intf, &usb_rio_class); kfree(rio->obuf); @@ -522,7 +522,6 @@ static struct usb_device_id rio_table [] = { MODULE_DEVICE_TABLE (usb, rio_table); static struct usb_driver rio_driver = { - .owner = THIS_MODULE, .name = "rio500", .probe = probe_rio, .disconnect = disconnect_rio, diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 41ef2b60675..3260d595441 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -863,9 +863,6 @@ static int sisusb_write_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, switch (length) { - case 0: - return ret; - case 1: if (userbuffer) { if (get_user(swap8, (u8 __user *)userbuffer)) @@ -1221,9 +1218,6 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr, switch (length) { - case 0: - return ret; - case 1: ret |= sisusb_read_memio_byte(sisusb, SISUSB_TYPE_MEM, @@ -2443,8 +2437,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) u8 *tempbuf; u16 *tempbufb; size_t written; - static char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer."; - static char bootlogo[] = "(o_ //\\ V_/_"; + static const char bootstring[] = "SiSUSB VGA text console, (C) 2005 Thomas Winischhofer."; + static const char bootlogo[] = "(o_ //\\ V_/_"; /* sisusb->lock is down */ @@ -3489,7 +3483,6 @@ static struct usb_device_id sisusb_table [] = { MODULE_DEVICE_TABLE (usb, sisusb_table); static struct usb_driver sisusb_driver = { - .owner = THIS_MODULE, .name = "sisusb", .probe = sisusb_probe, .disconnect = sisusb_disconnect, diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 85f3725334b..cc3dae3f34e 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -371,7 +371,6 @@ static void lcd_disconnect(struct usb_interface *interface) } static struct usb_driver lcd_driver = { - .owner = THIS_MODULE, .name = "usblcd", .probe = lcd_probe, .disconnect = lcd_disconnect, diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 3c93921cb6b..877b081a3a6 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -148,7 +148,6 @@ static void led_disconnect(struct usb_interface *interface) } static struct usb_driver led_driver = { - .owner = THIS_MODULE, .name = "usbled", .probe = led_probe, .disconnect = led_disconnect, diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 605a2afe34e..84fa1728f05 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2134,7 +2134,6 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver usbtest_driver = { - .owner = THIS_MODULE, .name = "usbtest", .id_table = id_table, .probe = usbtest_probe, diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 1cabe7ed91f..4081990b7d1 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -780,7 +780,6 @@ MODULE_DEVICE_TABLE (usb, uss720_table); static struct usb_driver uss720_driver = { - .owner = THIS_MODULE, .name = "uss720", .probe = uss720_probe, .disconnect = uss720_disconnect, diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index 17d0190ef64..611612146ae 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -97,19 +97,12 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, if (len >= DATA_MAX) len = DATA_MAX; - /* - * Bulk is easy to shortcut reliably. - * 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 (usb_pipein(pipe)) { + if (ev_type == 'S') + return '<'; + } else { + if (ev_type == 'C') + return '>'; } /* diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c index 542120ef1fd..54118169504 100644 --- a/drivers/usb/net/asix.c +++ b/drivers/usb/net/asix.c @@ -912,13 +912,16 @@ static const struct usb_device_id products [] = { // ASIX AX88772 10/100 USB_DEVICE (0x0b95, 0x7720), .driver_info = (unsigned long) &ax88772_info, +}, { + // Linksys USB200M Rev 2 + USB_DEVICE (0x13b1, 0x0018), + .driver_info = (unsigned long) &ax88772_info, }, { }, // END }; MODULE_DEVICE_TABLE(usb, products); static struct usb_driver asix_driver = { - .owner = THIS_MODULE, .name = "asix", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index 37ef365a247..be5f5e142dd 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -934,7 +934,6 @@ static struct usb_device_id catc_id_table [] = { MODULE_DEVICE_TABLE(usb, catc_id_table); static struct usb_driver catc_driver = { - .owner = THIS_MODULE, .name = driver_name, .probe = catc_probe, .disconnect = catc_disconnect, diff --git a/drivers/usb/net/cdc_ether.c b/drivers/usb/net/cdc_ether.c index c008c981862..63f1f3ba8e0 100644 --- a/drivers/usb/net/cdc_ether.c +++ b/drivers/usb/net/cdc_ether.c @@ -476,7 +476,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver cdc_driver = { - .owner = THIS_MODULE, .name = "cdc_ether", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/cdc_subset.c b/drivers/usb/net/cdc_subset.c index f05cfb83c82..ec801e8bb1b 100644 --- a/drivers/usb/net/cdc_subset.c +++ b/drivers/usb/net/cdc_subset.c @@ -306,7 +306,6 @@ MODULE_DEVICE_TABLE(usb, products); /*-------------------------------------------------------------------------*/ static struct usb_driver cdc_subset_driver = { - .owner = THIS_MODULE, .name = "cdc_subset", .probe = usbnet_probe, .suspend = usbnet_suspend, diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c index 2455e9a8567..faf1e86be68 100644 --- a/drivers/usb/net/gl620a.c +++ b/drivers/usb/net/gl620a.c @@ -377,7 +377,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver gl620a_driver = { - .owner = THIS_MODULE, .name = "gl620a", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index b5776518020..def3bb8e229 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -175,7 +175,6 @@ MODULE_DEVICE_TABLE (usb, usb_klsi_table); * kaweth_driver ****************************************************************/ static struct usb_driver kaweth_driver = { - .owner = THIS_MODULE, .name = driver_name, .probe = kaweth_probe, .disconnect = kaweth_disconnect, diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c index b3799b1a2b0..78e6a43b108 100644 --- a/drivers/usb/net/net1080.c +++ b/drivers/usb/net/net1080.c @@ -593,7 +593,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver net1080_driver = { - .owner = THIS_MODULE, .name = "net1080", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index 683e3df5d60..156a2f1cb39 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -45,7 +45,7 @@ /* * Version Information */ -#define DRIVER_VERSION "v0.6.12 (2005/01/13)" +#define DRIVER_VERSION "v0.6.13 (2005/11/13)" #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>" #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver" @@ -57,12 +57,14 @@ static const char driver_name[] = "pegasus"; static int loopback = 0; static int mii_mode = 0; +static char *devid=NULL; static struct usb_eth_dev usb_dev_id[] = { #define PEGASUS_DEV(pn, vid, pid, flags) \ {.name = pn, .vendor = vid, .device = pid, .private = flags}, #include "pegasus.h" #undef PEGASUS_DEV + {NULL, 0, 0, 0}, {NULL, 0, 0, 0} }; @@ -71,6 +73,7 @@ static struct usb_device_id pegasus_ids[] = { {.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid}, #include "pegasus.h" #undef PEGASUS_DEV + {}, {} }; @@ -79,8 +82,10 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(loopback, bool, 0); module_param(mii_mode, bool, 0); +module_param(devid, charp, 0); MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)"); MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0"); +MODULE_PARM_DESC(devid, "The format is: 'DEV_name:VendorID:DeviceID:Flags'"); /* use ethtool to change the level for any given device */ static int msg_level = -1; @@ -113,7 +118,7 @@ static void ctrl_callback(struct urb *urb, struct pt_regs *regs) break; default: if (netif_msg_drv(pegasus)) - dev_err(&pegasus->intf->dev, "%s, status %d\n", + dev_dbg(&pegasus->intf->dev, "%s, status %d\n", __FUNCTION__, urb->status); } pegasus->flags &= ~ETH_REGS_CHANGED; @@ -308,9 +313,9 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd) __le16 regdi; int ret; - ret = set_register(pegasus, PhyCtrl, 0); - ret = set_registers(pegasus, PhyAddr, sizeof (data), data); - ret = set_register(pegasus, PhyCtrl, (indx | PHY_READ)); + set_register(pegasus, PhyCtrl, 0); + set_registers(pegasus, PhyAddr, sizeof (data), data); + set_register(pegasus, PhyCtrl, (indx | PHY_READ)); for (i = 0; i < REG_TIMEOUT; i++) { ret = get_registers(pegasus, PhyCtrl, 1, data); if (data[0] & PHY_DONE) @@ -319,12 +324,12 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd) if (i < REG_TIMEOUT) { ret = get_registers(pegasus, PhyData, 2, ®di); *regd = le16_to_cpu(regdi); - return 1; + return ret; } if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); - return 0; + return ret; } static int mdio_read(struct net_device *dev, int phy_id, int loc) @@ -344,20 +349,20 @@ static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd) data[1] = (u8) regd; data[2] = (u8) (regd >> 8); - ret = set_register(pegasus, PhyCtrl, 0); - ret = set_registers(pegasus, PhyAddr, sizeof(data), data); - ret = set_register(pegasus, PhyCtrl, (indx | PHY_WRITE)); + set_register(pegasus, PhyCtrl, 0); + set_registers(pegasus, PhyAddr, sizeof(data), data); + set_register(pegasus, PhyCtrl, (indx | PHY_WRITE)); for (i = 0; i < REG_TIMEOUT; i++) { ret = get_registers(pegasus, PhyCtrl, 1, data); if (data[0] & PHY_DONE) break; } if (i < REG_TIMEOUT) - return 0; + return ret; if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); - return 1; + return -ETIMEDOUT; } static void mdio_write(struct net_device *dev, int phy_id, int loc, int val) @@ -374,9 +379,9 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata) __le16 retdatai; int ret; - ret = set_register(pegasus, EpromCtrl, 0); - ret = set_register(pegasus, EpromOffset, index); - ret = set_register(pegasus, EpromCtrl, EPROM_READ); + set_register(pegasus, EpromCtrl, 0); + set_register(pegasus, EpromOffset, index); + set_register(pegasus, EpromCtrl, EPROM_READ); for (i = 0; i < REG_TIMEOUT; i++) { ret = get_registers(pegasus, EpromCtrl, 1, &tmp); @@ -386,12 +391,12 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata) if (i < REG_TIMEOUT) { ret = get_registers(pegasus, EpromData, 2, &retdatai); *retdata = le16_to_cpu(retdatai); - return 0; + return ret; } if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); - return -1; + return -ETIMEDOUT; } #ifdef PEGASUS_WRITE_EEPROM @@ -400,8 +405,8 @@ static inline void enable_eprom_write(pegasus_t * pegasus) __u8 tmp; int ret; - ret = get_registers(pegasus, EthCtrl2, 1, &tmp); - ret = set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); + get_registers(pegasus, EthCtrl2, 1, &tmp); + set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE); } static inline void disable_eprom_write(pegasus_t * pegasus) @@ -409,9 +414,9 @@ static inline void disable_eprom_write(pegasus_t * pegasus) __u8 tmp; int ret; - ret = get_registers(pegasus, EthCtrl2, 1, &tmp); - ret = set_register(pegasus, EpromCtrl, 0); - ret = set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); + get_registers(pegasus, EthCtrl2, 1, &tmp); + set_register(pegasus, EpromCtrl, 0); + set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE); } static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) @@ -420,11 +425,11 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; int ret; - ret = set_registers(pegasus, EpromOffset, 4, d); + set_registers(pegasus, EpromOffset, 4, d); enable_eprom_write(pegasus); - ret = set_register(pegasus, EpromOffset, index); - ret = set_registers(pegasus, EpromData, 2, &data); - ret = set_register(pegasus, EpromCtrl, EPROM_WRITE); + set_register(pegasus, EpromOffset, index); + set_registers(pegasus, EpromData, 2, &data); + set_register(pegasus, EpromCtrl, EPROM_WRITE); for (i = 0; i < REG_TIMEOUT; i++) { ret = get_registers(pegasus, EpromCtrl, 1, &tmp); @@ -433,10 +438,10 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) } disable_eprom_write(pegasus); if (i < REG_TIMEOUT) - return 0; + return ret; if (netif_msg_drv(pegasus)) dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__); - return -1; + return -ETIMEDOUT; } #endif /* PEGASUS_WRITE_EEPROM */ @@ -454,10 +459,9 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id) static void set_ethernet_addr(pegasus_t * pegasus) { __u8 node_id[6]; - int ret; get_node_id(pegasus, node_id); - ret = set_registers(pegasus, EthID, sizeof (node_id), node_id); + set_registers(pegasus, EthID, sizeof (node_id), node_id); memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id)); } @@ -465,30 +469,29 @@ static inline int reset_mac(pegasus_t * pegasus) { __u8 data = 0x8; int i; - int ret; - ret = set_register(pegasus, EthCtrl1, data); + set_register(pegasus, EthCtrl1, data); for (i = 0; i < REG_TIMEOUT; i++) { - ret = get_registers(pegasus, EthCtrl1, 1, &data); + get_registers(pegasus, EthCtrl1, 1, &data); if (~data & 0x08) { if (loopback & 1) break; if (mii_mode && (pegasus->features & HAS_HOME_PNA)) - ret = set_register(pegasus, Gpio1, 0x34); + set_register(pegasus, Gpio1, 0x34); else - ret = set_register(pegasus, Gpio1, 0x26); - ret = set_register(pegasus, Gpio0, pegasus->features); - ret = set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); + set_register(pegasus, Gpio1, 0x26); + set_register(pegasus, Gpio0, pegasus->features); + set_register(pegasus, Gpio0, DEFAULT_GPIO_SET); break; } } if (i == REG_TIMEOUT) - return 1; + return -ETIMEDOUT; if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS || usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) { - ret = set_register(pegasus, Gpio0, 0x24); - ret = set_register(pegasus, Gpio0, 0x26); + set_register(pegasus, Gpio0, 0x24); + set_register(pegasus, Gpio0, 0x26); } if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) { __u16 auxmode; @@ -527,7 +530,7 @@ static int enable_net_traffic(struct net_device *dev, struct usb_device *usb) write_mii_word(pegasus, 0, 0x1b, auxmode | 4); } - return 0; + return ret; } static void fill_skb_pool(pegasus_t * pegasus) @@ -881,9 +884,8 @@ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) static inline void disable_net_traffic(pegasus_t * pegasus) { int tmp = 0; - int ret; - ret = set_registers(pegasus, EthCtrl0, 2, &tmp); + set_registers(pegasus, EthCtrl0, 2, &tmp); } static inline void get_interrupt_interval(pegasus_t * pegasus) @@ -1206,18 +1208,17 @@ static __u8 mii_phy_probe(pegasus_t * pegasus) static inline void setup_pegasus_II(pegasus_t * pegasus) { __u8 data = 0xa5; - int ret; - ret = set_register(pegasus, Reg1d, 0); - ret = set_register(pegasus, Reg7b, 1); + set_register(pegasus, Reg1d, 0); + set_register(pegasus, Reg7b, 1); mdelay(100); if ((pegasus->features & HAS_HOME_PNA) && mii_mode) - ret = set_register(pegasus, Reg7b, 0); + set_register(pegasus, Reg7b, 0); else - ret = set_register(pegasus, Reg7b, 2); + set_register(pegasus, Reg7b, 2); - ret = set_register(pegasus, 0x83, data); - ret = get_registers(pegasus, 0x83, 1, &data); + set_register(pegasus, 0x83, data); + get_registers(pegasus, 0x83, 1, &data); if (data == 0xa5) { pegasus->chip = 0x8513; @@ -1225,14 +1226,14 @@ static inline void setup_pegasus_II(pegasus_t * pegasus) pegasus->chip = 0; } - ret = set_register(pegasus, 0x80, 0xc0); - ret = set_register(pegasus, 0x83, 0xff); - ret = set_register(pegasus, 0x84, 0x01); + set_register(pegasus, 0x80, 0xc0); + set_register(pegasus, 0x83, 0xff); + set_register(pegasus, 0x84, 0x01); if (pegasus->features & HAS_HOME_PNA && mii_mode) - ret = set_register(pegasus, Reg81, 6); + set_register(pegasus, Reg81, 6); else - ret = set_register(pegasus, Reg81, 2); + set_register(pegasus, Reg81, 2); } @@ -1414,9 +1415,42 @@ static struct usb_driver pegasus_driver = { .resume = pegasus_resume, }; +static void parse_id(char *id) +{ + unsigned int vendor_id=0, device_id=0, flags=0, i=0; + char *token, *name=NULL; + + if ((token = strsep(&id, ":")) != NULL) + name = token; + /* name now points to a null terminated string*/ + if ((token = strsep(&id, ":")) != NULL) + vendor_id = simple_strtoul(token, NULL, 16); + if ((token = strsep(&id, ":")) != NULL) + device_id = simple_strtoul(token, NULL, 16); + flags = simple_strtoul(id, NULL, 16); + pr_info("%s: new device %s, vendor ID 0x%04x, device ID 0x%04x, flags: 0x%x\n", + driver_name, name, vendor_id, device_id, flags); + + if (vendor_id > 0x10000 || vendor_id == 0) + return; + if (device_id > 0x10000 || device_id == 0) + return; + + for (i=0; usb_dev_id[i].name; i++); + usb_dev_id[i].name = name; + usb_dev_id[i].vendor = vendor_id; + usb_dev_id[i].device = device_id; + usb_dev_id[i].private = flags; + pegasus_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; + pegasus_ids[i].idVendor = vendor_id; + pegasus_ids[i].idProduct = device_id; +} + static int __init pegasus_init(void) { pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION); + if (devid) + parse_id(devid); pegasus_workqueue = create_singlethread_workqueue("pegasus"); if (!pegasus_workqueue) return -ENOMEM; diff --git a/drivers/usb/net/plusb.c b/drivers/usb/net/plusb.c index 89856aa0e3b..4fe863389cb 100644 --- a/drivers/usb/net/plusb.c +++ b/drivers/usb/net/plusb.c @@ -127,7 +127,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver plusb_driver = { - .owner = THIS_MODULE, .name = "plusb", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index c0ecbab6f6b..49991ac1bf3 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c @@ -586,7 +586,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver rndis_driver = { - .owner = THIS_MODULE, .name = "rndis_host", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index 787dd3591d6..8ca52be2397 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -177,7 +177,6 @@ static int rtl8150_probe(struct usb_interface *intf, static const char driver_name [] = "rtl8150"; static struct usb_driver rtl8150_driver = { - .owner = THIS_MODULE, .name = driver_name, .probe = rtl8150_probe, .disconnect = rtl8150_disconnect, diff --git a/drivers/usb/net/zaurus.c b/drivers/usb/net/zaurus.c index 680d13957af..9c5ab251370 100644 --- a/drivers/usb/net/zaurus.c +++ b/drivers/usb/net/zaurus.c @@ -357,7 +357,6 @@ static const struct usb_device_id products [] = { MODULE_DEVICE_TABLE(usb, products); static struct usb_driver zaurus_driver = { - .owner = THIS_MODULE, .name = "zaurus", .id_table = products, .probe = usbnet_probe, diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c index 2f52261c7cc..f3a8e2807c3 100644 --- a/drivers/usb/net/zd1201.c +++ b/drivers/usb/net/zd1201.c @@ -1722,7 +1722,7 @@ static const struct iw_priv_args zd1201_private_args[] = { IW_PRIV_TYPE_NONE, "sethostauth" }, { ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" }, - { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, + { ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE, "authstation" }, { ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE, "setmaxassoc" }, @@ -1731,9 +1731,9 @@ static const struct iw_priv_args zd1201_private_args[] = { }; static const struct iw_handler_def zd1201_iw_handlers = { - .num_standard = sizeof(zd1201_iw_handler)/sizeof(iw_handler), - .num_private = sizeof(zd1201_private_handler)/sizeof(iw_handler), - .num_private_args = sizeof(zd1201_private_args)/sizeof(struct iw_priv_args), + .num_standard = ARRAY_SIZE(zd1201_iw_handler), + .num_private = ARRAY_SIZE(zd1201_private_handler), + .num_private_args = ARRAY_SIZE(zd1201_private_args), .standard = (iw_handler *)zd1201_iw_handler, .private = (iw_handler *)zd1201_private_handler, .private_args = (struct iw_priv_args *) zd1201_private_args, @@ -1829,6 +1829,8 @@ static int zd1201_probe(struct usb_interface *interface, if (err) goto err_net; + SET_NETDEV_DEV(zd->dev, &usb->dev); + err = register_netdev(zd->dev); if (err) goto err_net; @@ -1923,7 +1925,6 @@ static int zd1201_resume(struct usb_interface *interface) #endif static struct usb_driver zd1201_usb = { - .owner = THIS_MODULE, .name = "zd1201", .probe = zd1201_probe, .disconnect = zd1201_disconnect, diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 1f29d883732..dbf1f063098 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -23,11 +23,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver airprime_driver = { - .owner = THIS_MODULE, .name = "airprime", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver airprime_device = { diff --git a/drivers/usb/serial/anydata.c b/drivers/usb/serial/anydata.c index 18022a74a3d..343f6f22822 100644 --- a/drivers/usb/serial/anydata.c +++ b/drivers/usb/serial/anydata.c @@ -27,11 +27,11 @@ static int buffer_size; static int debug; static struct usb_driver anydata_driver = { - .owner = THIS_MODULE, .name = "anydata", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static int anydata_open(struct usb_serial_port *port, struct file *filp) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 84bc0ee4f06..4144777ea18 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -113,11 +113,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver belkin_driver = { - .owner = THIS_MODULE, .name = "belkin", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; /* All of the device info needed for the serial converters */ diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index c9787001cf2..da46b351e18 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -67,11 +67,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver cp2101_driver = { - .owner = THIS_MODULE, .name = "cp2101", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver cp2101_device = { diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index e581e4ae848..6d18d4eaba3 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -76,11 +76,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver cyberjack_driver = { - .owner = THIS_MODULE, .name = "cyberjack", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver cyberjack_device = { diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index af9290ed257..af18355e94c 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -112,6 +112,7 @@ static struct usb_driver cypress_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; struct cypress_private { diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index dc74644a603..8fc414bd5b2 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -493,11 +493,11 @@ static struct usb_device_id id_table_4 [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver digi_driver = { - .owner = THIS_MODULE, .name = "digi_acceleport", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 0b0546dcc7b..79a766e9ca2 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -105,11 +105,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver empeg_driver = { - .owner = THIS_MODULE, .name = "empeg", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver empeg_device = { diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 06e04b442ff..eb863b3f2d7 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -471,12 +471,15 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y6_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MHAM_Y8_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_VCP_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TERATRONIK_D2XX_PID) }, { USB_DEVICE(EVOLUTION_VID, EVOLUTION_ER1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ARTEMIS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ATIK_ATK16HR_PID) }, { USB_DEVICE(KOBIL_VID, KOBIL_CONV_B1_PID) }, { USB_DEVICE(KOBIL_VID, KOBIL_CONV_KAAN_PID) }, + { USB_DEVICE(POSIFLEX_VID, POSIFLEX_PP7000_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -488,9 +491,10 @@ static struct usb_driver ftdi_driver = { .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; -static char *ftdi_chip_name[] = { +static const char *ftdi_chip_name[] = { [SIO] = "SIO", /* the serial part of FT8U100AX */ [FT8U232AM] = "FT8U232AM", [FT232BM] = "FT232BM", diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 773ea3eca08..f380f9eaff7 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -343,6 +343,13 @@ #define XSENS_CONVERTER_7_PID 0xD38F /* + * Teratronik product ids. + * Submitted by O. Wölfelschneider. + */ +#define FTDI_TERATRONIK_VCP_PID 0xEC88 /* Teratronik device (preferring VCP driver on windows) */ +#define FTDI_TERATRONIK_D2XX_PID 0xEC89 /* Teratronik device (preferring D2XX driver on windows) */ + +/* * Evolution Robotics products (http://www.evolution.com/). * Submitted by Shawn M. Lavelle. */ @@ -352,6 +359,12 @@ /* Pyramid Computer GmbH */ #define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ +/* + * Posiflex inc retail equipment (http://www.posiflex.com.tw) + */ +#define POSIFLEX_VID 0x0d3a /* Vendor ID */ +#define POSIFLEX_PP7000_PID 0x0300 /* PP-7000II thermal printer */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 35820bda7ae..452efce7271 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -222,11 +222,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver garmin_driver = { - .owner = THIS_MODULE, .name = "garmin_gps", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 53a47c31cd0..4ddac620fc0 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -68,11 +68,11 @@ static int generic_probe(struct usb_interface *interface, } static struct usb_driver generic_driver = { - .owner = THIS_MODULE, .name = "usbserial_generic", .probe = generic_probe, .disconnect = usb_serial_disconnect, .id_table = generic_serial_ids, + .no_dynamic_id = 1, }; #endif diff --git a/drivers/usb/serial/hp4x.c b/drivers/usb/serial/hp4x.c index 8eadfb70560..e9719da2aca 100644 --- a/drivers/usb/serial/hp4x.c +++ b/drivers/usb/serial/hp4x.c @@ -37,11 +37,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE(usb, id_table); static struct usb_driver hp49gp_driver = { - .owner = THIS_MODULE, .name = "hp4X", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver hp49gp_device = { diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index dc4c498bd1e..faedbeb6ba4 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -184,7 +184,7 @@ struct divisor_table_entry { // These assume a 3.6864MHz crystal, the standard /16, and // MCR.7 = 0. // -static struct divisor_table_entry divisor_table[] = { +static const struct divisor_table_entry divisor_table[] = { { 50, 4608}, { 75, 3072}, { 110, 2095}, /* 2094.545455 => 230450 => .0217 % over */ @@ -242,11 +242,11 @@ static void edge_shutdown (struct usb_serial *serial); #include "io_tables.h" /* all of the devices that this driver supports */ static struct usb_driver io_driver = { - .owner = THIS_MODULE, .name = "io_edgeport", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; /* function prototypes for all of our local functions */ @@ -2353,7 +2353,7 @@ static int calc_baud_rate_divisor (int baudrate, int *divisor) dbg("%s - %d", __FUNCTION__, baudrate); - for (i = 0; i < NUM_ENTRIES(divisor_table); i++) { + for (i = 0; i < ARRAY_SIZE(divisor_table); i++) { if ( divisor_table[i].BaudRate == baudrate ) { *divisor = divisor_table[i].Divisor; return 0; diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 5112d7aac05..123fa8a904e 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -31,9 +31,6 @@ #ifndef HIGH8 #define HIGH8(a) ((unsigned char)((a & 0xff00) >> 8)) #endif -#ifndef NUM_ENTRIES - #define NUM_ENTRIES(x) (sizeof(x)/sizeof((x)[0])) -#endif #ifndef __KERNEL__ #define __KERNEL__ diff --git a/drivers/usb/serial/io_fw_boot2.h b/drivers/usb/serial/io_fw_boot2.h index c7c3a3c305f..e3463de99de 100644 --- a/drivers/usb/serial/io_fw_boot2.h +++ b/drivers/usb/serial/io_fw_boot2.h @@ -537,7 +537,7 @@ static unsigned char IMAGE_ARRAY_NAME[] = { }; -static struct edge_firmware_version_info IMAGE_VERSION_NAME = { +static const struct edge_firmware_version_info IMAGE_VERSION_NAME = { 2, 0, 3 }; // Major, Minor, Build #undef IMAGE_VERSION_NAME diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 832b6d6734c..2edf9cabad2 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -216,11 +216,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver io_driver = { - .owner = THIS_MODULE, .name = "io_ti", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; @@ -2843,7 +2843,7 @@ static struct edge_buf *edge_buf_alloc(unsigned int size) * Free the buffer and all associated memory. */ -void edge_buf_free(struct edge_buf *eb) +static void edge_buf_free(struct edge_buf *eb) { if (eb) { kfree(eb->buf_buf); diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index d5d06648810..06d07cea0b7 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -542,11 +542,11 @@ static struct usb_device_id ipaq_id_table [] = { MODULE_DEVICE_TABLE (usb, ipaq_id_table); static struct usb_driver ipaq_driver = { - .owner = THIS_MODULE, .name = "ipaq", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = ipaq_id_table, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 7744b8148bc..2dd191f5fe7 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -152,11 +152,11 @@ static struct usb_device_id usb_ipw_ids[] = { MODULE_DEVICE_TABLE(usb, usb_ipw_ids); static struct usb_driver usb_ipw_driver = { - .owner = THIS_MODULE, .name = "ipwtty", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = usb_ipw_ids, + .no_dynamic_id = 1, }; static int debug; diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index 19f329e9bdc..a5901042144 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -125,11 +125,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver ir_driver = { - .owner = THIS_MODULE, .name = "ir-usb", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h index 5cfc13b5e56..7472ed6bf62 100644 --- a/drivers/usb/serial/keyspan.h +++ b/drivers/usb/serial/keyspan.h @@ -520,11 +520,11 @@ static struct usb_device_id keyspan_ids_combined[] = { MODULE_DEVICE_TABLE(usb, keyspan_ids_combined); static struct usb_driver keyspan_driver = { - .owner = THIS_MODULE, .name = "keyspan", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = keyspan_ids_combined, + .no_dynamic_id = 1, }; /* usb_device_id table for the pre-firmware download keyspan devices */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index cd4f48bd83b..b0441c35f98 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -150,11 +150,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver keyspan_pda_driver = { - .owner = THIS_MODULE, .name = "keyspan_pda", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; static struct usb_device_id id_table_std [] = { diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index a8951c0fd02..4e2f7dfb58b 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -116,11 +116,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver kl5kusb105d_driver = { - .owner = THIS_MODULE, .name = "kl5kusb105d", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; static struct usb_serial_driver kl5kusb105d_device = { diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 9456dd9dd13..d9c21e27513 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -97,11 +97,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver kobil_driver = { - .owner = THIS_MODULE, .name = "kobil", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index ca5dbadb9b7..b6d6cab9c85 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -125,11 +125,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver mct_u232_driver = { - .owner = THIS_MODULE, .name = "mct_u232", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; static struct usb_serial_driver mct_u232_device = { diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 3caf97072ac..762d8ff9a1e 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -80,11 +80,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver omninet_driver = { - .owner = THIS_MODULE, .name = "omninet", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7716000045b..3fd2405304f 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -95,11 +95,11 @@ static struct usb_device_id option_ids[] = { MODULE_DEVICE_TABLE(usb, option_ids); static struct usb_driver option_driver = { - .owner = THIS_MODULE, .name = "option", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = option_ids, + .no_dynamic_id = 1, }; /* The card has three separate interfaces, wich the serial driver diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 41a45a5025b..f0372105619 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -82,11 +82,11 @@ static struct usb_device_id id_table [] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver pl2303_driver = { - .owner = THIS_MODULE, .name = "pl2303", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; #define SET_LINE_REQUEST_TYPE 0x21 @@ -810,7 +810,7 @@ static void pl2303_update_line_status(struct usb_serial_port *port, struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned long flags; u8 status_idx = UART_STATE; - u8 length = UART_STATE; + u8 length = UART_STATE + 1; if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) && (le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 || diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index c22bdc0c4df..f0215f850d2 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -160,14 +160,14 @@ static struct usb_device_id id_table[] = { MODULE_DEVICE_TABLE (usb, id_table); static struct usb_driver safe_driver = { - .owner = THIS_MODULE, .name = "safe_serial", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table, + .no_dynamic_id = 1, }; -static __u16 crc10_table[256] = { +static const __u16 crc10_table[256] = { 0x000, 0x233, 0x255, 0x066, 0x299, 0x0aa, 0x0cc, 0x2ff, 0x301, 0x132, 0x154, 0x367, 0x198, 0x3ab, 0x3cd, 0x1fe, 0x031, 0x202, 0x264, 0x057, 0x2a8, 0x09b, 0x0fd, 0x2ce, 0x330, 0x103, 0x165, 0x356, 0x1a9, 0x39a, 0x3fc, 0x1cf, 0x062, 0x251, 0x237, 0x004, 0x2fb, 0x0c8, 0x0ae, 0x29d, 0x363, 0x150, 0x136, 0x305, 0x1fa, 0x3c9, 0x3af, 0x19c, @@ -425,7 +425,7 @@ static int __init safe_init (void) if (vendor || product) { info ("vendor: %x product: %x\n", vendor, product); - for (i = 0; i < (sizeof (id_table) / sizeof (struct usb_device_id)); i++) { + for (i = 0; i < ARRAY_SIZE(id_table); i++) { if (!id_table[i].idVendor && !id_table[i].idProduct) { id_table[i].idVendor = vendor; id_table[i].idProduct = product; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 205dbf7201d..abb830cb77b 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -248,11 +248,11 @@ static struct usb_device_id ti_id_table_combined[] = { }; static struct usb_driver ti_usb_driver = { - .owner = THIS_MODULE, .name = "ti_usb_3410_5052", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = ti_id_table_combined, + .no_dynamic_id = 1, }; static struct usb_serial_driver ti_1port_device = { @@ -351,17 +351,14 @@ static int __init ti_init(void) int i,j; int ret; - /* insert extra vendor and product ids */ - j = sizeof(ti_id_table_3410)/sizeof(struct usb_device_id) - - TI_EXTRA_VID_PID_COUNT - 1; + j = ARRAY_SIZE(ti_id_table_3410) - TI_EXTRA_VID_PID_COUNT - 1; for (i=0; i<min(vendor_3410_count,product_3410_count); i++,j++) { ti_id_table_3410[j].idVendor = vendor_3410[i]; ti_id_table_3410[j].idProduct = product_3410[i]; ti_id_table_3410[j].match_flags = USB_DEVICE_ID_MATCH_DEVICE; } - j = sizeof(ti_id_table_5052)/sizeof(struct usb_device_id) - - TI_EXTRA_VID_PID_COUNT - 1; + j = ARRAY_SIZE(ti_id_table_5052) - TI_EXTRA_VID_PID_COUNT - 1; for (i=0; i<min(vendor_5052_count,product_5052_count); i++,j++) { ti_id_table_5052[j].idVendor = vendor_5052[i]; ti_id_table_5052[j].idProduct = product_5052[i]; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 0c4881d18cd..8bc8337c99c 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -30,6 +30,7 @@ #include <linux/list.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> +#include <asm/semaphore.h> #include <linux/usb.h> #include "usb-serial.h" #include "pl2303.h" @@ -42,10 +43,10 @@ /* Driver structure we register with the USB core */ static struct usb_driver usb_serial_driver = { - .owner = THIS_MODULE, .name = "usbserial", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, + .no_dynamic_id = 1, }; /* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead @@ -188,6 +189,11 @@ static int serial_open (struct tty_struct *tty, struct file * filp) portNumber = tty->index - serial->minor; port = serial->port[portNumber]; + if (!port) + return -ENODEV; + + if (down_interruptible(&port->sem)) + return -ERESTARTSYS; ++port->open_count; @@ -213,6 +219,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) goto bailout_module_put; } + up(&port->sem); return 0; bailout_module_put: @@ -220,6 +227,7 @@ bailout_module_put: bailout_kref_put: kref_put(&serial->kref, destroy_serial); port->open_count = 0; + up(&port->sem); return retval; } @@ -232,8 +240,10 @@ static void serial_close(struct tty_struct *tty, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); + down(&port->sem); + if (port->open_count == 0) - return; + goto out; --port->open_count; if (port->open_count == 0) { @@ -251,6 +261,9 @@ static void serial_close(struct tty_struct *tty, struct file * filp) } kref_put(&port->serial->kref, destroy_serial); + +out: + up(&port->sem); } static int serial_write (struct tty_struct * tty, const unsigned char *buf, int count) @@ -258,6 +271,9 @@ static int serial_write (struct tty_struct * tty, const unsigned char *buf, int struct usb_serial_port *port = tty->driver_data; int retval = -EINVAL; + if (!port) + goto exit; + dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); if (!port->open_count) { @@ -277,6 +293,9 @@ static int serial_write_room (struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; int retval = -EINVAL; + if (!port) + goto exit; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -296,6 +315,9 @@ static int serial_chars_in_buffer (struct tty_struct *tty) struct usb_serial_port *port = tty->driver_data; int retval = -EINVAL; + if (!port) + goto exit; + dbg("%s = port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -314,6 +336,9 @@ static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = tty->driver_data; + if (!port) + return; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -330,6 +355,9 @@ static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = tty->driver_data; + if (!port) + return; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -347,6 +375,9 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in struct usb_serial_port *port = tty->driver_data; int retval = -ENODEV; + if (!port) + goto exit; + dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd); if (!port->open_count) { @@ -368,6 +399,9 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) { struct usb_serial_port *port = tty->driver_data; + if (!port) + return; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -384,6 +418,9 @@ static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + if (!port) + return; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -445,6 +482,9 @@ static int serial_tiocmget (struct tty_struct *tty, struct file *file) { struct usb_serial_port *port = tty->driver_data; + if (!port) + goto exit; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -464,6 +504,9 @@ static int serial_tiocmset (struct tty_struct *tty, struct file *file, { struct usb_serial_port *port = tty->driver_data; + if (!port) + goto exit; + dbg("%s - port %d", __FUNCTION__, port->number); if (!port->open_count) { @@ -742,6 +785,7 @@ int usb_serial_probe(struct usb_interface *interface, port->number = i + serial->minor; port->serial = serial; spin_lock_init(&port->lock); + sema_init(&port->sem, 1); INIT_WORK(&port->work, usb_serial_port_softint, port); serial->port[i] = port; } diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h index 238a5a871ed..d7d27c3385b 100644 --- a/drivers/usb/serial/usb-serial.h +++ b/drivers/usb/serial/usb-serial.h @@ -16,6 +16,7 @@ #include <linux/config.h> #include <linux/kref.h> +#include <asm/semaphore.h> #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ #define SERIAL_TTY_MINORS 255 /* loads of devices :) */ @@ -30,6 +31,8 @@ * @serial: pointer back to the struct usb_serial owner of this port. * @tty: pointer to the corresponding tty for this port. * @lock: spinlock to grab when updating portions of this structure. + * @sem: semaphore used to synchronize serial_open() and serial_close() + * access for this port. * @number: the number of the port (the minor number). * @interrupt_in_buffer: pointer to the interrupt in buffer for this port. * @interrupt_in_urb: pointer to the interrupt in struct urb for this port. @@ -60,6 +63,7 @@ struct usb_serial_port { struct usb_serial * serial; struct tty_struct * tty; spinlock_t lock; + struct semaphore sem; unsigned char number; unsigned char * interrupt_in_buffer; diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index a473c1c3455..49b1fbe61f2 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -173,11 +173,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver visor_driver = { - .owner = THIS_MODULE, .name = "visor", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; /* All of the device info needed for the Handspring Visor, and Palm 4.0 devices */ diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 18c3183be76..a7c3c4734d8 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -127,11 +127,11 @@ static struct usb_device_id id_table_combined [] = { MODULE_DEVICE_TABLE (usb, id_table_combined); static struct usb_driver whiteheat_driver = { - .owner = THIS_MODULE, .name = "whiteheat", .probe = usb_serial_probe, .disconnect = usb_serial_disconnect, .id_table = id_table_combined, + .no_dynamic_id = 1, }; /* function prototypes for the Connect Tech WhiteHEAT prerenumeration device */ diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index c41d64dbb0f..92be101feba 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -112,6 +112,15 @@ config USB_STORAGE_JUMPSHOT Say Y here to include additional code to support the Lexar Jumpshot USB CompactFlash reader. +config USB_STORAGE_ALAUDA + bool "Olympus MAUSB-10/Fuji DPC-R1 support (EXPERIMENTAL)" + depends on USB_STORAGE && EXPERIMENTAL + help + Say Y here to include additional code to support the Olympus MAUSB-10 + and Fujifilm DPC-R1 USB Card reader/writer devices. + + These devices are based on the Alauda chip and support support both + XD and SmartMedia cards. config USB_STORAGE_ONETOUCH bool "Support OneTouch Button on Maxtor Hard Drives (EXPERIMENTAL)" @@ -124,3 +133,17 @@ config USB_STORAGE_ONETOUCH hard drive's as an input device. An action can be associated with this input in any keybinding software. (e.g. gnome's keyboard short- cuts) + +config USB_LIBUSUAL + bool "The shared table of common (or usual) storage devices" + depends on USB + help + This module contains a table of common (or usual) devices + for usb-storage and ub drivers, and allows to switch binding + of these devices without rebuilding modules. + + Typical syntax of /etc/modprobe.conf is: + + options libusual bias="ub" + + If unsure, say N. diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 44ab8f9978f..8cbba22508a 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -18,7 +18,12 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o +usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ initializers.o $(usb-storage-obj-y) + +ifneq ($(CONFIG_USB_LIBUSUAL),) + obj-$(CONFIG_USB) += libusual.o +endif diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c new file mode 100644 index 00000000000..4d3cbb12b71 --- /dev/null +++ b/drivers/usb/storage/alauda.c @@ -0,0 +1,1119 @@ +/* + * Driver for Alauda-based card readers + * + * Current development and maintenance by: + * (c) 2005 Daniel Drake <dsd@gentoo.org> + * + * The 'Alauda' is a chip manufacturered by RATOC for OEM use. + * + * Alauda implements a vendor-specific command set to access two media reader + * ports (XD, SmartMedia). This driver converts SCSI commands to the commands + * which are accepted by these devices. + * + * The driver was developed through reverse-engineering, with the help of the + * sddr09 driver which has many similarities, and with some help from the + * (very old) vendor-supplied GPL sma03 driver. + * + * For protocol info, see http://alauda.sourceforge.net + * + * 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 + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> + +#include "usb.h" +#include "transport.h" +#include "protocol.h" +#include "debug.h" +#include "alauda.h" + +#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) ) +#define LSB_of(s) ((s)&0xFF) +#define MSB_of(s) ((s)>>8) + +#define MEDIA_PORT(us) us->srb->device->lun +#define MEDIA_INFO(us) ((struct alauda_info *)us->extra)->port[MEDIA_PORT(us)] + +#define PBA_LO(pba) ((pba & 0xF) << 5) +#define PBA_HI(pba) (pba >> 3) +#define PBA_ZONE(pba) (pba >> 11) + +/* + * Media handling + */ + +struct alauda_card_info { + unsigned char id; /* id byte */ + unsigned char chipshift; /* 1<<cs bytes total capacity */ + unsigned char pageshift; /* 1<<ps bytes in a page */ + unsigned char blockshift; /* 1<<bs pages per block */ + unsigned char zoneshift; /* 1<<zs blocks per zone */ +}; + +static struct alauda_card_info alauda_card_ids[] = { + /* NAND flash */ + { 0x6e, 20, 8, 4, 8}, /* 1 MB */ + { 0xe8, 20, 8, 4, 8}, /* 1 MB */ + { 0xec, 20, 8, 4, 8}, /* 1 MB */ + { 0x64, 21, 8, 4, 9}, /* 2 MB */ + { 0xea, 21, 8, 4, 9}, /* 2 MB */ + { 0x6b, 22, 9, 4, 9}, /* 4 MB */ + { 0xe3, 22, 9, 4, 9}, /* 4 MB */ + { 0xe5, 22, 9, 4, 9}, /* 4 MB */ + { 0xe6, 23, 9, 4, 10}, /* 8 MB */ + { 0x73, 24, 9, 5, 10}, /* 16 MB */ + { 0x75, 25, 9, 5, 10}, /* 32 MB */ + { 0x76, 26, 9, 5, 10}, /* 64 MB */ + { 0x79, 27, 9, 5, 10}, /* 128 MB */ + { 0x71, 28, 9, 5, 10}, /* 256 MB */ + + /* MASK ROM */ + { 0x5d, 21, 9, 4, 8}, /* 2 MB */ + { 0xd5, 22, 9, 4, 9}, /* 4 MB */ + { 0xd6, 23, 9, 4, 10}, /* 8 MB */ + { 0x57, 24, 9, 4, 11}, /* 16 MB */ + { 0x58, 25, 9, 4, 12}, /* 32 MB */ + { 0,} +}; + +static struct alauda_card_info *alauda_card_find_id(unsigned char id) { + int i; + + for (i = 0; alauda_card_ids[i].id != 0; i++) + if (alauda_card_ids[i].id == id) + return &(alauda_card_ids[i]); + return NULL; +} + +/* + * ECC computation. + */ + +static unsigned char parity[256]; +static unsigned char ecc2[256]; + +static void nand_init_ecc(void) { + int i, j, a; + + parity[0] = 0; + for (i = 1; i < 256; i++) + parity[i] = (parity[i&(i-1)] ^ 1); + + for (i = 0; i < 256; i++) { + a = 0; + for (j = 0; j < 8; j++) { + if (i & (1<<j)) { + if ((j & 1) == 0) + a ^= 0x04; + if ((j & 2) == 0) + a ^= 0x10; + if ((j & 4) == 0) + a ^= 0x40; + } + } + ecc2[i] = ~(a ^ (a<<1) ^ (parity[i] ? 0xa8 : 0)); + } +} + +/* compute 3-byte ecc on 256 bytes */ +static void nand_compute_ecc(unsigned char *data, unsigned char *ecc) { + int i, j, a; + unsigned char par, bit, bits[8]; + + par = 0; + for (j = 0; j < 8; j++) + bits[j] = 0; + + /* collect 16 checksum bits */ + for (i = 0; i < 256; i++) { + par ^= data[i]; + bit = parity[data[i]]; + for (j = 0; j < 8; j++) + if ((i & (1<<j)) == 0) + bits[j] ^= bit; + } + + /* put 4+4+4 = 12 bits in the ecc */ + a = (bits[3] << 6) + (bits[2] << 4) + (bits[1] << 2) + bits[0]; + ecc[0] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); + + a = (bits[7] << 6) + (bits[6] << 4) + (bits[5] << 2) + bits[4]; + ecc[1] = ~(a ^ (a<<1) ^ (parity[par] ? 0xaa : 0)); + + ecc[2] = ecc2[par]; +} + +static int nand_compare_ecc(unsigned char *data, unsigned char *ecc) { + return (data[0] == ecc[0] && data[1] == ecc[1] && data[2] == ecc[2]); +} + +static void nand_store_ecc(unsigned char *data, unsigned char *ecc) { + memcpy(data, ecc, 3); +} + +/* + * Alauda driver + */ + +/* + * Forget our PBA <---> LBA mappings for a particular port + */ +static void alauda_free_maps (struct alauda_media_info *media_info) +{ + unsigned int shift = media_info->zoneshift + + media_info->blockshift + media_info->pageshift; + unsigned int num_zones = media_info->capacity >> shift; + unsigned int i; + + if (media_info->lba_to_pba != NULL) + for (i = 0; i < num_zones; i++) { + kfree(media_info->lba_to_pba[i]); + media_info->lba_to_pba[i] = NULL; + } + + if (media_info->pba_to_lba != NULL) + for (i = 0; i < num_zones; i++) { + kfree(media_info->pba_to_lba[i]); + media_info->pba_to_lba[i] = NULL; + } +} + +/* + * Returns 2 bytes of status data + * The first byte describes media status, and second byte describes door status + */ +static int alauda_get_media_status(struct us_data *us, unsigned char *data) +{ + int rc; + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_GET_XD_MEDIA_STATUS; + else + command = ALAUDA_GET_SM_MEDIA_STATUS; + + rc = usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, + command, 0xc0, 0, 1, data, 2); + + US_DEBUGP("alauda_get_media_status: Media status %02X %02X\n", + data[0], data[1]); + + return rc; +} + +/* + * Clears the "media was changed" bit so that we know when it changes again + * in the future. + */ +static int alauda_ack_media(struct us_data *us) +{ + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_ACK_XD_MEDIA_CHANGE; + else + command = ALAUDA_ACK_SM_MEDIA_CHANGE; + + return usb_stor_ctrl_transfer(us, us->send_ctrl_pipe, + command, 0x40, 0, 1, NULL, 0); +} + +/* + * Retrieves a 4-byte media signature, which indicates manufacturer, capacity, + * and some other details. + */ +static int alauda_get_media_signature(struct us_data *us, unsigned char *data) +{ + unsigned char command; + + if (MEDIA_PORT(us) == ALAUDA_PORT_XD) + command = ALAUDA_GET_XD_MEDIA_SIG; + else + command = ALAUDA_GET_SM_MEDIA_SIG; + + return usb_stor_ctrl_transfer(us, us->recv_ctrl_pipe, + command, 0xc0, 0, 0, data, 4); +} + +/* + * Resets the media status (but not the whole device?) + */ +static int alauda_reset_media(struct us_data *us) +{ + unsigned char *command = us->iobuf; + + memset(command, 0, 9); + command[0] = ALAUDA_BULK_CMD; + command[1] = ALAUDA_BULK_RESET_MEDIA; + command[8] = MEDIA_PORT(us); + + return usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); +} + +/* + * Examines the media and deduces capacity, etc. + */ +static int alauda_init_media(struct us_data *us) +{ + unsigned char *data = us->iobuf; + int ready = 0; + struct alauda_card_info *media_info; + unsigned int num_zones; + + while (ready == 0) { + msleep(20); + + if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (data[0] & 0x10) + ready = 1; + } + + US_DEBUGP("alauda_init_media: We are ready for action!\n"); + + if (alauda_ack_media(us) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + msleep(10); + + if (alauda_get_media_status(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + if (data[0] != 0x14) { + US_DEBUGP("alauda_init_media: Media not ready after ack\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + if (alauda_get_media_signature(us, data) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUGP("alauda_init_media: Media signature: %02X %02X %02X %02X\n", + data[0], data[1], data[2], data[3]); + media_info = alauda_card_find_id(data[1]); + if (media_info == NULL) { + printk("alauda_init_media: Unrecognised media signature: " + "%02X %02X %02X %02X\n", + data[0], data[1], data[2], data[3]); + return USB_STOR_TRANSPORT_ERROR; + } + + MEDIA_INFO(us).capacity = 1 << media_info->chipshift; + US_DEBUGP("Found media with capacity: %ldMB\n", + MEDIA_INFO(us).capacity >> 20); + + MEDIA_INFO(us).pageshift = media_info->pageshift; + MEDIA_INFO(us).blockshift = media_info->blockshift; + MEDIA_INFO(us).zoneshift = media_info->zoneshift; + + MEDIA_INFO(us).pagesize = 1 << media_info->pageshift; + MEDIA_INFO(us).blocksize = 1 << media_info->blockshift; + MEDIA_INFO(us).zonesize = 1 << media_info->zoneshift; + + MEDIA_INFO(us).uzonesize = ((1 << media_info->zoneshift) / 128) * 125; + MEDIA_INFO(us).blockmask = MEDIA_INFO(us).blocksize - 1; + + num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift + + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); + MEDIA_INFO(us).pba_to_lba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); + MEDIA_INFO(us).lba_to_pba = kcalloc(num_zones, sizeof(u16*), GFP_NOIO); + + if (alauda_reset_media(us) != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Examines the media status and does the right thing when the media has gone, + * appeared, or changed. + */ +static int alauda_check_media(struct us_data *us) +{ + struct alauda_info *info = (struct alauda_info *) us->extra; + unsigned char status[2]; + int rc; + + rc = alauda_get_media_status(us, status); + + /* Check for no media or door open */ + if ((status[0] & 0x80) || ((status[0] & 0x1F) == 0x10) + || ((status[1] & 0x01) == 0)) { + US_DEBUGP("alauda_check_media: No media, or door open\n"); + alauda_free_maps(&MEDIA_INFO(us)); + info->sense_key = 0x02; + info->sense_asc = 0x3A; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + /* Check for media change */ + if (status[0] & 0x08) { + US_DEBUGP("alauda_check_media: Media change detected\n"); + alauda_free_maps(&MEDIA_INFO(us)); + alauda_init_media(us); + + info->sense_key = UNIT_ATTENTION; + info->sense_asc = 0x28; + info->sense_ascq = 0x00; + return USB_STOR_TRANSPORT_FAILED; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Checks the status from the 2nd status register + * Returns 3 bytes of status data, only the first is known + */ +static int alauda_check_status2(struct us_data *us) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_GET_STATUS2, + 0, 0, 0, 0, 3, 0, MEDIA_PORT(us) + }; + unsigned char data[3]; + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, 3, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + US_DEBUGP("alauda_check_status2: %02X %02X %02X\n", data[0], data[1], data[2]); + if (data[0] & ALAUDA_STATUS_ERROR) + return USB_STOR_XFER_ERROR; + + return USB_STOR_XFER_GOOD; +} + +/* + * Gets the redundancy data for the first page of a PBA + * Returns 16 bytes. + */ +static int alauda_get_redu_data(struct us_data *us, u16 pba, unsigned char *data) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_GET_REDU_DATA, + PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0, 0, MEDIA_PORT(us) + }; + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, 16, NULL); +} + +/* + * Finds the first unused PBA in a zone + * Returns the absolute PBA of an unused PBA, or 0 if none found. + */ +static u16 alauda_find_unused_pba(struct alauda_media_info *info, + unsigned int zone) +{ + u16 *pba_to_lba = info->pba_to_lba[zone]; + unsigned int i; + + for (i = 0; i < info->zonesize; i++) + if (pba_to_lba[i] == UNDEF) + return (zone << info->zoneshift) + i; + + return 0; +} + +/* + * Reads the redundancy data for all PBA's in a zone + * Produces lba <--> pba mappings + */ +static int alauda_read_map(struct us_data *us, unsigned int zone) +{ + unsigned char *data = us->iobuf; + int result; + int i, j; + unsigned int zonesize = MEDIA_INFO(us).zonesize; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + unsigned int lba_offset, lba_real, blocknum; + unsigned int zone_base_lba = zone * uzonesize; + unsigned int zone_base_pba = zone * zonesize; + u16 *lba_to_pba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); + u16 *pba_to_lba = kcalloc(zonesize, sizeof(u16), GFP_NOIO); + if (lba_to_pba == NULL || pba_to_lba == NULL) { + result = USB_STOR_TRANSPORT_ERROR; + goto error; + } + + US_DEBUGP("alauda_read_map: Mapping blocks for zone %d\n", zone); + + /* 1024 PBA's per zone */ + for (i = 0; i < zonesize; i++) + lba_to_pba[i] = pba_to_lba[i] = UNDEF; + + for (i = 0; i < zonesize; i++) { + blocknum = zone_base_pba + i; + + result = alauda_get_redu_data(us, blocknum, data); + if (result != USB_STOR_XFER_GOOD) { + result = USB_STOR_TRANSPORT_ERROR; + goto error; + } + + /* special PBAs have control field 0^16 */ + for (j = 0; j < 16; j++) + if (data[j] != 0) + goto nonz; + pba_to_lba[i] = UNUSABLE; + US_DEBUGP("alauda_read_map: PBA %d has no logical mapping\n", blocknum); + continue; + + nonz: + /* unwritten PBAs have control field FF^16 */ + for (j = 0; j < 16; j++) + if (data[j] != 0xff) + goto nonff; + continue; + + nonff: + /* normal PBAs start with six FFs */ + if (j < 6) { + US_DEBUGP("alauda_read_map: PBA %d has no logical mapping: " + "reserved area = %02X%02X%02X%02X " + "data status %02X block status %02X\n", + blocknum, data[0], data[1], data[2], data[3], + data[4], data[5]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + if ((data[6] >> 4) != 0x01) { + US_DEBUGP("alauda_read_map: PBA %d has invalid address " + "field %02X%02X/%02X%02X\n", + blocknum, data[6], data[7], data[11], data[12]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + /* check even parity */ + if (parity[data[6] ^ data[7]]) { + printk("alauda_read_map: Bad parity in LBA for block %d" + " (%02X %02X)\n", i, data[6], data[7]); + pba_to_lba[i] = UNUSABLE; + continue; + } + + lba_offset = short_pack(data[7], data[6]); + lba_offset = (lba_offset & 0x07FF) >> 1; + lba_real = lba_offset + zone_base_lba; + + /* + * Every 1024 physical blocks ("zone"), the LBA numbers + * go back to zero, but are within a higher block of LBA's. + * Also, there is a maximum of 1000 LBA's per zone. + * In other words, in PBA 1024-2047 you will find LBA 0-999 + * which are really LBA 1000-1999. This allows for 24 bad + * or special physical blocks per zone. + */ + + if (lba_offset >= uzonesize) { + printk("alauda_read_map: Bad low LBA %d for block %d\n", + lba_real, blocknum); + continue; + } + + if (lba_to_pba[lba_offset] != UNDEF) { + printk("alauda_read_map: LBA %d seen for PBA %d and %d\n", + lba_real, lba_to_pba[lba_offset], blocknum); + continue; + } + + pba_to_lba[i] = lba_real; + lba_to_pba[lba_offset] = blocknum; + continue; + } + + MEDIA_INFO(us).lba_to_pba[zone] = lba_to_pba; + MEDIA_INFO(us).pba_to_lba[zone] = pba_to_lba; + result = 0; + goto out; + +error: + kfree(lba_to_pba); + kfree(pba_to_lba); +out: + return result; +} + +/* + * Checks to see whether we have already mapped a certain zone + * If we haven't, the map is generated + */ +static void alauda_ensure_map_for_zone(struct us_data *us, unsigned int zone) +{ + if (MEDIA_INFO(us).lba_to_pba[zone] == NULL + || MEDIA_INFO(us).pba_to_lba[zone] == NULL) + alauda_read_map(us, zone); +} + +/* + * Erases an entire block + */ +static int alauda_erase_block(struct us_data *us, u16 pba) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, MEDIA_PORT(us) + }; + unsigned char buf[2]; + + US_DEBUGP("alauda_erase_block: Erasing PBA %d\n", pba); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + buf, 2, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + US_DEBUGP("alauda_erase_block: Erase result: %02X %02X\n", + buf[0], buf[1]); + return rc; +} + +/* + * Reads data from a certain offset page inside a PBA, including interleaved + * redundancy data. Returns (pagesize+64)*pages bytes in data. + */ +static int alauda_read_block_raw(struct us_data *us, u16 pba, + unsigned int page, unsigned int pages, unsigned char *data) +{ + int rc; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_READ_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba) + page, pages, 0, MEDIA_PORT(us) + }; + + US_DEBUGP("alauda_read_block: pba %d page %d count %d\n", + pba, page, pages); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, + data, (MEDIA_INFO(us).pagesize + 64) * pages, NULL); +} + +/* + * Reads data from a certain offset page inside a PBA, excluding redundancy + * data. Returns pagesize*pages bytes in data. Note that data must be big enough + * to hold (pagesize+64)*pages bytes of data, but you can ignore those 'extra' + * trailing bytes outside this function. + */ +static int alauda_read_block(struct us_data *us, u16 pba, + unsigned int page, unsigned int pages, unsigned char *data) +{ + int i, rc; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + + rc = alauda_read_block_raw(us, pba, page, pages, data); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + /* Cut out the redundancy data */ + for (i = 0; i < pages; i++) { + int dest_offset = i * pagesize; + int src_offset = i * (pagesize + 64); + memmove(data + dest_offset, data + src_offset, pagesize); + } + + return rc; +} + +/* + * Writes an entire block of data and checks status after write. + * Redundancy data must be already included in data. Data should be + * (pagesize+64)*blocksize bytes in length. + */ +static int alauda_write_block(struct us_data *us, u16 pba, unsigned char *data) +{ + int rc; + struct alauda_info *info = (struct alauda_info *) us->extra; + unsigned char command[] = { + ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_BLOCK, PBA_HI(pba), + PBA_ZONE(pba), 0, PBA_LO(pba), 32, 0, MEDIA_PORT(us) + }; + + US_DEBUGP("alauda_write_block: pba %d\n", pba); + + rc = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + command, 9, NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + rc = usb_stor_bulk_transfer_buf(us, info->wr_ep, data, + (MEDIA_INFO(us).pagesize + 64) * MEDIA_INFO(us).blocksize, + NULL); + if (rc != USB_STOR_XFER_GOOD) + return rc; + + return alauda_check_status2(us); +} + +/* + * Write some data to a specific LBA. + */ +static int alauda_write_lba(struct us_data *us, u16 lba, + unsigned int page, unsigned int pages, + unsigned char *ptr, unsigned char *blockbuffer) +{ + u16 pba, lbap, new_pba; + unsigned char *bptr, *cptr, *xptr; + unsigned char ecc[3]; + int i, result; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + unsigned int zonesize = MEDIA_INFO(us).zonesize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int lba_offset = lba % uzonesize; + unsigned int new_pba_offset; + unsigned int zone = lba / uzonesize; + + alauda_ensure_map_for_zone(us, zone); + + pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; + if (pba == 1) { + /* Maybe it is impossible to write to PBA 1. + Fake success, but don't do anything. */ + printk("alauda_write_lba: avoid writing to pba 1\n"); + return USB_STOR_TRANSPORT_GOOD; + } + + new_pba = alauda_find_unused_pba(&MEDIA_INFO(us), zone); + if (!new_pba) { + printk("alauda_write_lba: Out of unused blocks\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* read old contents */ + if (pba != UNDEF) { + result = alauda_read_block_raw(us, pba, 0, + blocksize, blockbuffer); + if (result != USB_STOR_XFER_GOOD) + return result; + } else { + memset(blockbuffer, 0, blocksize * (pagesize + 64)); + } + + lbap = (lba_offset << 1) | 0x1000; + if (parity[MSB_of(lbap) ^ LSB_of(lbap)]) + lbap ^= 1; + + /* check old contents and fill lba */ + for (i = 0; i < blocksize; i++) { + bptr = blockbuffer + (i * (pagesize + 64)); + cptr = bptr + pagesize; + nand_compute_ecc(bptr, ecc); + if (!nand_compare_ecc(cptr+13, ecc)) { + US_DEBUGP("Warning: bad ecc in page %d- of pba %d\n", + i, pba); + nand_store_ecc(cptr+13, ecc); + } + nand_compute_ecc(bptr + (pagesize / 2), ecc); + if (!nand_compare_ecc(cptr+8, ecc)) { + US_DEBUGP("Warning: bad ecc in page %d+ of pba %d\n", + i, pba); + nand_store_ecc(cptr+8, ecc); + } + cptr[6] = cptr[11] = MSB_of(lbap); + cptr[7] = cptr[12] = LSB_of(lbap); + } + + /* copy in new stuff and compute ECC */ + xptr = ptr; + for (i = page; i < page+pages; i++) { + bptr = blockbuffer + (i * (pagesize + 64)); + cptr = bptr + pagesize; + memcpy(bptr, xptr, pagesize); + xptr += pagesize; + nand_compute_ecc(bptr, ecc); + nand_store_ecc(cptr+13, ecc); + nand_compute_ecc(bptr + (pagesize / 2), ecc); + nand_store_ecc(cptr+8, ecc); + } + + result = alauda_write_block(us, new_pba, blockbuffer); + if (result != USB_STOR_XFER_GOOD) + return result; + + new_pba_offset = new_pba - (zone * zonesize); + MEDIA_INFO(us).pba_to_lba[zone][new_pba_offset] = lba; + MEDIA_INFO(us).lba_to_pba[zone][lba_offset] = new_pba; + US_DEBUGP("alauda_write_lba: Remapped LBA %d to PBA %d\n", + lba, new_pba); + + if (pba != UNDEF) { + unsigned int pba_offset = pba - (zone * zonesize); + result = alauda_erase_block(us, pba); + if (result != USB_STOR_XFER_GOOD) + return result; + MEDIA_INFO(us).pba_to_lba[zone][pba_offset] = UNDEF; + } + + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Read data from a specific sector address + */ +static int alauda_read_data(struct us_data *us, unsigned long address, + unsigned int sectors) +{ + unsigned char *buffer; + u16 lba, max_lba; + unsigned int page, len, index, offset; + unsigned int blockshift = MEDIA_INFO(us).blockshift; + unsigned int pageshift = MEDIA_INFO(us).pageshift; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + unsigned int uzonesize = MEDIA_INFO(us).uzonesize; + int result; + + /* + * Since we only read in one block 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. + * We make this buffer big enough to hold temporary redundancy data, + * which we use when reading the data blocks. + */ + + len = min(sectors, blocksize) * (pagesize + 64); + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) { + printk("alauda_read_data: Out of memory\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* Figure out the initial LBA and page */ + lba = address >> blockshift; + page = (address & MEDIA_INFO(us).blockmask); + max_lba = MEDIA_INFO(us).capacity >> (blockshift + pageshift); + + result = USB_STOR_TRANSPORT_GOOD; + index = offset = 0; + + while (sectors > 0) { + unsigned int zone = lba / uzonesize; /* integer division */ + unsigned int lba_offset = lba - (zone * uzonesize); + unsigned int pages; + u16 pba; + alauda_ensure_map_for_zone(us, zone); + + /* Not overflowing capacity? */ + if (lba >= max_lba) { + US_DEBUGP("Error: Requested lba %u exceeds " + "maximum %u\n", lba, max_lba); + result = USB_STOR_TRANSPORT_ERROR; + break; + } + + /* Find number of pages we can read in this block */ + pages = min(sectors, blocksize - page); + len = pages << pageshift; + + /* Find where this lba lives on disk */ + pba = MEDIA_INFO(us).lba_to_pba[zone][lba_offset]; + + if (pba == UNDEF) { /* this lba was never written */ + US_DEBUGP("Read %d zero pages (LBA %d) page %d\n", + pages, lba, page); + + /* This is not really an error. It just means + that the block has never been written. + Instead of returning USB_STOR_TRANSPORT_ERROR + it is better to return all zero data. */ + + memset(buffer, 0, len); + } else { + US_DEBUGP("Read %d pages, from PBA %d" + " (LBA %d) page %d\n", + pages, pba, lba, page); + + result = alauda_read_block(us, pba, page, pages, buffer); + if (result != USB_STOR_TRANSPORT_GOOD) + break; + } + + /* Store the data in the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, TO_XFER_BUF); + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + return result; +} + +/* + * Write data to a specific sector address + */ +static int alauda_write_data(struct us_data *us, unsigned long address, + unsigned int sectors) +{ + unsigned char *buffer, *blockbuffer; + unsigned int page, len, index, offset; + unsigned int blockshift = MEDIA_INFO(us).blockshift; + unsigned int pageshift = MEDIA_INFO(us).pageshift; + unsigned int blocksize = MEDIA_INFO(us).blocksize; + unsigned int pagesize = MEDIA_INFO(us).pagesize; + u16 lba, max_lba; + int result; + + /* + * Since we don't write the user data directly to the device, + * 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. + */ + + len = min(sectors, blocksize) * pagesize; + buffer = kmalloc(len, GFP_NOIO); + if (buffer == NULL) { + printk("alauda_write_data: Out of memory\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* + * We also need a temporary block buffer, where we read in the old data, + * overwrite parts with the new data, and manipulate the redundancy data + */ + blockbuffer = kmalloc((pagesize + 64) * blocksize, GFP_NOIO); + if (blockbuffer == NULL) { + printk("alauda_write_data: Out of memory\n"); + kfree(buffer); + return USB_STOR_TRANSPORT_ERROR; + } + + /* Figure out the initial LBA and page */ + lba = address >> blockshift; + page = (address & MEDIA_INFO(us).blockmask); + max_lba = MEDIA_INFO(us).capacity >> (pageshift + blockshift); + + result = USB_STOR_TRANSPORT_GOOD; + index = offset = 0; + + while (sectors > 0) { + /* Write as many sectors as possible in this block */ + unsigned int pages = min(sectors, blocksize - page); + len = pages << pageshift; + + /* Not overflowing capacity? */ + if (lba >= max_lba) { + US_DEBUGP("alauda_write_data: Requested lba %u exceeds " + "maximum %u\n", lba, max_lba); + result = USB_STOR_TRANSPORT_ERROR; + break; + } + + /* Get the data from the transfer buffer */ + usb_stor_access_xfer_buf(buffer, len, us->srb, + &index, &offset, FROM_XFER_BUF); + + result = alauda_write_lba(us, lba, page, pages, buffer, + blockbuffer); + if (result != USB_STOR_TRANSPORT_GOOD) + break; + + page = 0; + lba++; + sectors -= pages; + } + + kfree(buffer); + kfree(blockbuffer); + return result; +} + +/* + * Our interface with the rest of the world + */ + +static void alauda_info_destructor(void *extra) +{ + struct alauda_info *info = (struct alauda_info *) extra; + int port; + + if (!info) + return; + + for (port = 0; port < 2; port++) { + struct alauda_media_info *media_info = &info->port[port]; + + alauda_free_maps(media_info); + kfree(media_info->lba_to_pba); + kfree(media_info->pba_to_lba); + } +} + +/* + * Initialize alauda_info struct and find the data-write endpoint + */ +int init_alauda(struct us_data *us) +{ + struct alauda_info *info; + struct usb_host_interface *altsetting = us->pusb_intf->cur_altsetting; + nand_init_ecc(); + + us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO); + if (!us->extra) { + US_DEBUGP("init_alauda: Gah! Can't allocate storage for" + "alauda info struct!\n"); + return USB_STOR_TRANSPORT_ERROR; + } + info = (struct alauda_info *) us->extra; + us->extra_destructor = alauda_info_destructor; + + info->wr_ep = usb_sndbulkpipe(us->pusb_dev, + altsetting->endpoint[0].desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + return USB_STOR_TRANSPORT_GOOD; +} + +int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) +{ + int rc; + struct alauda_info *info = (struct alauda_info *) us->extra; + 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("alauda_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] == TEST_UNIT_READY) { + US_DEBUGP("alauda_transport: TEST_UNIT_READY.\n"); + return alauda_check_media(us); + } + + if (srb->cmnd[0] == READ_CAPACITY) { + unsigned int num_zones; + unsigned long capacity; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + num_zones = MEDIA_INFO(us).capacity >> (MEDIA_INFO(us).zoneshift + + MEDIA_INFO(us).blockshift + MEDIA_INFO(us).pageshift); + + capacity = num_zones * MEDIA_INFO(us).uzonesize + * MEDIA_INFO(us).blocksize; + + /* Report capacity and page size */ + ((__be32 *) ptr)[0] = cpu_to_be32(capacity - 1); + ((__be32 *) ptr)[1] = cpu_to_be32(512); + + usb_stor_set_xfer_buf(ptr, 8, srb); + return USB_STOR_TRANSPORT_GOOD; + } + + if (srb->cmnd[0] == READ_10) { + unsigned int page, pages; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + US_DEBUGP("alauda_transport: READ_10: page %d pagect %d\n", + page, pages); + + return alauda_read_data(us, page, pages); + } + + if (srb->cmnd[0] == WRITE_10) { + unsigned int page, pages; + + rc = alauda_check_media(us); + if (rc != USB_STOR_TRANSPORT_GOOD) + return rc; + + page = short_pack(srb->cmnd[3], srb->cmnd[2]); + page <<= 16; + page |= short_pack(srb->cmnd[5], srb->cmnd[4]); + pages = short_pack(srb->cmnd[8], srb->cmnd[7]); + + US_DEBUGP("alauda_transport: WRITE_10: page %d pagect %d\n", + page, pages); + + return alauda_write_data(us, page, pages); + } + + if (srb->cmnd[0] == REQUEST_SENSE) { + US_DEBUGP("alauda_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("alauda_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; +} + diff --git a/drivers/usb/storage/alauda.h b/drivers/usb/storage/alauda.h new file mode 100644 index 00000000000..a700f87d080 --- /dev/null +++ b/drivers/usb/storage/alauda.h @@ -0,0 +1,100 @@ +/* + * Driver for Alauda-based card readers + * + * Current development and maintenance by: + * (c) 2005 Daniel Drake <dsd@gentoo.org> + * + * See alauda.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 + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _USB_ALAUDA_H +#define _USB_ALAUDA_H + +/* + * Status bytes + */ +#define ALAUDA_STATUS_ERROR 0x01 +#define ALAUDA_STATUS_READY 0x40 + +/* + * Control opcodes (for request field) + */ +#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 +#define ALAUDA_GET_SM_MEDIA_STATUS 0x98 +#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a +#define ALAUDA_ACK_SM_MEDIA_CHANGE 0x9a +#define ALAUDA_GET_XD_MEDIA_SIG 0x86 +#define ALAUDA_GET_SM_MEDIA_SIG 0x96 + +/* + * Bulk command identity (byte 0) + */ +#define ALAUDA_BULK_CMD 0x40 + +/* + * Bulk opcodes (byte 1) + */ +#define ALAUDA_BULK_GET_REDU_DATA 0x85 +#define ALAUDA_BULK_READ_BLOCK 0x94 +#define ALAUDA_BULK_ERASE_BLOCK 0xa3 +#define ALAUDA_BULK_WRITE_BLOCK 0xb4 +#define ALAUDA_BULK_GET_STATUS2 0xb7 +#define ALAUDA_BULK_RESET_MEDIA 0xe0 + +/* + * Port to operate on (byte 8) + */ +#define ALAUDA_PORT_XD 0x00 +#define ALAUDA_PORT_SM 0x01 + +/* + * LBA and PBA are unsigned ints. Special values. + */ +#define UNDEF 0xffff +#define SPARE 0xfffe +#define UNUSABLE 0xfffd + +int init_alauda(struct us_data *us); +int alauda_transport(struct scsi_cmnd *srb, struct us_data *us); + +struct alauda_media_info { + unsigned long capacity; /* total media size in bytes */ + unsigned int pagesize; /* page size in bytes */ + unsigned int blocksize; /* number of pages per block */ + unsigned int uzonesize; /* number of usable blocks per zone */ + unsigned int zonesize; /* number of blocks per zone */ + unsigned int blockmask; /* mask to get page from address */ + + unsigned char pageshift; + unsigned char blockshift; + unsigned char zoneshift; + + u16 **lba_to_pba; /* logical to physical block map */ + u16 **pba_to_lba; /* physical to logical block map */ +}; + +struct alauda_info { + struct alauda_media_info port[2]; + int wr_ep; /* endpoint to write data out of */ + + unsigned char sense_key; + unsigned long sense_asc; /* additional sense code */ + unsigned long sense_ascq; /* additional sense code qualifier */ +}; + +#endif + diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 5a9321705a7..01e430654a1 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -132,6 +132,7 @@ void usb_stor_show_command(struct scsi_cmnd *srb) case 0x5C: what = "READ BUFFER CAPACITY"; break; case 0x5D: what = "SEND CUE SHEET"; break; case GPCMD_BLANK: what = "BLANK"; break; + case REPORT_LUNS: what = "REPORT LUNS"; break; case MOVE_MEDIUM: what = "MOVE_MEDIUM or PLAY AUDIO (12)"; break; case READ_12: what = "READ_12"; break; case WRITE_12: what = "WRITE_12"; break; diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 7372386f33d..4c1b2bd2e2e 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -45,10 +45,6 @@ * mode */ int usb_stor_euscsi_init(struct us_data *us); -#ifdef CONFIG_USB_STORAGE_SDDR09 -int sddr09_init(struct us_data *us); -#endif - /* This function is required to activate all four slots on the UCR-61S2B * flash reader */ int usb_stor_ucr61s2b_init(struct us_data *us); diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c new file mode 100644 index 00000000000..b28151d1b60 --- /dev/null +++ b/drivers/usb/storage/libusual.c @@ -0,0 +1,266 @@ +/* + * libusual + * + * The libusual contains the table of devices common for ub and usb-storage. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb_usual.h> +#include <linux/vmalloc.h> + +/* + */ +#define USU_MOD_FL_THREAD 1 /* Thread is running */ +#define USU_MOD_FL_PRESENT 2 /* The module is loaded */ + +struct mod_status { + unsigned long fls; +}; + +static struct mod_status stat[3]; +static DEFINE_SPINLOCK(usu_lock); + +/* + */ +#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR +static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS); + +#define BIAS_NAME_SIZE (sizeof("usb-storage")) +static const char *bias_names[3] = { "none", "usb-storage", "ub" }; + +static DECLARE_MUTEX_LOCKED(usu_init_notify); +static DECLARE_COMPLETION(usu_end_notify); +static atomic_t total_threads = ATOMIC_INIT(0); + +static int usu_probe_thread(void *arg); + +/* + * The table. + */ +#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ + vendorName, productName,useProtocol, useTransport, \ + initFunction, flags) \ +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ + .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + +#define USUAL_DEV(useProto, useTrans, useType) \ +{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ + .driver_info = ((useType)<<24) } + +struct usb_device_id storage_usb_ids [] = { +# include "unusual_devs.h" + { } /* Terminating entry */ +}; + +#undef USUAL_DEV +#undef UNUSUAL_DEV + +MODULE_DEVICE_TABLE(usb, storage_usb_ids); +EXPORT_SYMBOL_GPL(storage_usb_ids); + +/* + * @type: the module type as an integer + */ +void usb_usual_set_present(int type) +{ + struct mod_status *st; + unsigned long flags; + + if (type <= 0 || type >= 3) + return; + st = &stat[type]; + spin_lock_irqsave(&usu_lock, flags); + st->fls |= USU_MOD_FL_PRESENT; + spin_unlock_irqrestore(&usu_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_usual_set_present); + +void usb_usual_clear_present(int type) +{ + struct mod_status *st; + unsigned long flags; + + if (type <= 0 || type >= 3) + return; + st = &stat[type]; + spin_lock_irqsave(&usu_lock, flags); + st->fls &= ~USU_MOD_FL_PRESENT; + spin_unlock_irqrestore(&usu_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_usual_clear_present); + +/* + * Match the calling driver type against the table. + * Returns: 0 if the device matches. + */ +int usb_usual_check_type(const struct usb_device_id *id, int caller_type) +{ + int id_type = USB_US_TYPE(id->driver_info); + + if (caller_type <= 0 || caller_type >= 3) + return -EINVAL; + + /* Drivers grab fixed assignment devices */ + if (id_type == caller_type) + return 0; + /* Drivers grab devices biased to them */ + if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias)) + return 0; + return -ENODEV; +} +EXPORT_SYMBOL_GPL(usb_usual_check_type); + +/* + */ +static int usu_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int type; + int rc; + unsigned long flags; + + type = USB_US_TYPE(id->driver_info); + if (type == 0) + type = atomic_read(&usu_bias); + + spin_lock_irqsave(&usu_lock, flags); + if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) { + spin_unlock_irqrestore(&usu_lock, flags); + return -ENXIO; + } + stat[type].fls |= USU_MOD_FL_THREAD; + spin_unlock_irqrestore(&usu_lock, flags); + + rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM); + if (rc < 0) { + printk(KERN_WARNING "libusual: " + "Unable to start the thread for %s: %d\n", + bias_names[type], rc); + spin_lock_irqsave(&usu_lock, flags); + stat[type].fls &= ~USU_MOD_FL_THREAD; + spin_unlock_irqrestore(&usu_lock, flags); + return rc; /* Not being -ENXIO causes a message printed */ + } + atomic_inc(&total_threads); + + return -ENXIO; +} + +static void usu_disconnect(struct usb_interface *intf) +{ + ; /* We should not be here. */ +} + +static struct usb_driver usu_driver = { + .name = "libusual", + .probe = usu_probe, + .disconnect = usu_disconnect, + .id_table = storage_usb_ids, +}; + +/* + * A whole new thread for a purpose of request_module seems quite stupid. + * The request_module forks once inside again. However, if we attempt + * to load a storage module from our own modprobe thread, that module + * references our symbols, which cannot be resolved until our module is + * initialized. I wish there was a way to wait for the end of initialization. + * The module notifier reports MODULE_STATE_COMING only. + * So, we wait until module->init ends as the next best thing. + */ +static int usu_probe_thread(void *arg) +{ + int type = (unsigned long) arg; + struct mod_status *st = &stat[type]; + int rc; + unsigned long flags; + + daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */ + + /* A completion does not work here because it's counted. */ + down(&usu_init_notify); + up(&usu_init_notify); + + rc = request_module(bias_names[type]); + spin_lock_irqsave(&usu_lock, flags); + if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) { + /* + * This should not happen, but let us keep tabs on it. + */ + printk(KERN_NOTICE "libusual: " + "modprobe for %s succeeded, but module is not present\n", + bias_names[type]); + } + st->fls &= ~USU_MOD_FL_THREAD; + spin_unlock_irqrestore(&usu_lock, flags); + + complete_and_exit(&usu_end_notify, 0); +} + +/* + */ +static int __init usb_usual_init(void) +{ + int rc; + + rc = usb_register(&usu_driver); + up(&usu_init_notify); + return rc; +} + +static void __exit usb_usual_exit(void) +{ + /* + * We do not check for any drivers present, because + * they keep us pinned with symbol references. + */ + + usb_deregister(&usu_driver); + + while (atomic_read(&total_threads) > 0) { + wait_for_completion(&usu_end_notify); + atomic_dec(&total_threads); + } +} + +/* + * Validate and accept the bias parameter. + */ +static int usu_set_bias(const char *bias_s, struct kernel_param *kp) +{ + int i; + int len; + int bias_n = 0; + + len = strlen(bias_s); + if (len == 0) + return -EDOM; + if (bias_s[len-1] == '\n') + --len; + + for (i = 1; i < 3; i++) { + if (strncmp(bias_s, bias_names[i], len) == 0) { + bias_n = i; + break; + } + } + if (bias_n == 0) + return -EINVAL; + + atomic_set(&usu_bias, bias_n); + return 0; +} + +static int usu_get_bias(char *buffer, struct kernel_param *kp) +{ + return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)])); +} + +module_init(usb_usual_init); +module_exit(usb_usual_exit); + +module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR); +__MODULE_PARM_TYPE(bias, "string"); +MODULE_PARM_DESC(bias, "Bias to usb-storage or ub"); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 89401a59f95..55ee2d36d58 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -52,6 +52,7 @@ struct usb_onetouch { struct urb *irq; /* urb for interrupt in report */ unsigned char *data; /* input data */ dma_addr_t data_dma; + unsigned int is_open:1; }; static void usb_onetouch_irq(struct urb *urb, struct pt_regs *regs) @@ -89,6 +90,7 @@ static int usb_onetouch_open(struct input_dev *dev) { struct usb_onetouch *onetouch = dev->private; + onetouch->is_open = 1; onetouch->irq->dev = onetouch->udev; if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { err("usb_submit_urb failed"); @@ -103,8 +105,30 @@ static void usb_onetouch_close(struct input_dev *dev) struct usb_onetouch *onetouch = dev->private; usb_kill_urb(onetouch->irq); + onetouch->is_open = 0; } +#ifdef CONFIG_PM +static void usb_onetouch_pm_hook(struct us_data *us, int action) +{ + struct usb_onetouch *onetouch = (struct usb_onetouch *) us->extra; + + if (onetouch->is_open) { + switch (action) { + case US_SUSPEND: + usb_kill_urb(onetouch->irq); + break; + case US_RESUME: + if (usb_submit_urb(onetouch->irq, GFP_KERNEL) != 0) + err("usb_submit_urb failed"); + break; + default: + break; + } + } +} +#endif /* CONFIG_PM */ + int onetouch_connect_input(struct us_data *ss) { struct usb_device *udev = ss->pusb_dev; @@ -185,6 +209,9 @@ int onetouch_connect_input(struct us_data *ss) ss->extra_destructor = onetouch_release_input; ss->extra = onetouch; +#ifdef CONFIG_PM + ss->suspend_resume_hook = usb_onetouch_pm_hook; +#endif input_register_device(onetouch->dev); diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index 02bff01ab09..845bed4b803 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -41,20 +41,6 @@ #ifndef _PROTOCOL_H_ #define _PROTOCOL_H_ -/* Sub Classes */ - -#define US_SC_RBC 0x01 /* Typically, flash devices */ -#define US_SC_8020 0x02 /* CD-ROM */ -#define US_SC_QIC 0x03 /* QIC-157 Tapes */ -#define US_SC_UFI 0x04 /* Floppy */ -#define US_SC_8070 0x05 /* Removable media */ -#define US_SC_SCSI 0x06 /* Transparent */ -#define US_SC_ISD200 0x07 /* ISD200 ATA */ -#define US_SC_MIN US_SC_RBC -#define US_SC_MAX US_SC_ISD200 - -#define US_SC_DEVICE 0xff /* Use device's value */ - /* Protocol handling routines */ extern void usb_stor_ATAPI_command(struct scsi_cmnd*, struct us_data*); extern void usb_stor_qic157_command(struct scsi_cmnd*, struct us_data*); diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 0ea2f5ab66b..fb8bacaae27 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -133,13 +133,11 @@ static struct nand_flash_dev nand_flash_ids[] = { { 0,} }; -#define SIZE(a) (sizeof(a)/sizeof((a)[0])) - static struct nand_flash_dev * nand_find_id(unsigned char id) { int i; - for (i = 0; i < SIZE(nand_flash_ids); i++) + for (i = 0; i < ARRAY_SIZE(nand_flash_ids); i++) if (nand_flash_ids[i].model_id == id) return &(nand_flash_ids[i]); return NULL; @@ -214,6 +212,20 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) { * The actual driver starts here. */ +struct sddr09_card_info { + unsigned long capacity; /* Size of card in bytes */ + int pagesize; /* Size of page in bytes */ + int pageshift; /* log2 of pagesize */ + int blocksize; /* Size of block in pages */ + int blockshift; /* log2 of blocksize */ + int blockmask; /* 2^blockshift - 1 */ + int *lba_to_pba; /* logical to physical map */ + int *pba_to_lba; /* physical to logical map */ + int lbact; /* number of available pages */ + int flags; +#define SDDR09_WP 1 /* write protected */ +}; + /* * On my 16MB card, control blocks have size 64 (16 real control bytes, * and 48 junk bytes). In reality of course the card uses 16 control bytes, @@ -237,7 +249,7 @@ static void nand_store_ecc(unsigned char *data, unsigned char *ecc) { #define SPARE 0xfffffffe #define UNUSABLE 0xfffffffd -static int erase_bad_lba_entries = 0; +static const int erase_bad_lba_entries = 0; /* send vendor interface command (0x41) */ /* called for requests 0, 1, 8 */ @@ -260,8 +272,11 @@ sddr09_send_command(struct us_data *us, rc = usb_stor_ctrl_transfer(us, pipe, request, requesttype, 0, 0, xfer_data, xfer_len); - return (rc == USB_STOR_XFER_GOOD ? USB_STOR_TRANSPORT_GOOD : - USB_STOR_TRANSPORT_ERROR); + switch (rc) { + case USB_STOR_XFER_GOOD: return 0; + case USB_STOR_XFER_STALLED: return -EPIPE; + default: return -EIO; + } } static int @@ -308,20 +323,12 @@ sddr09_request_sense(struct us_data *us, unsigned char *sensebuf, int buflen) { command[4] = buflen; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { - US_DEBUGP("request sense failed\n"); + if (result) return result; - } result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, sensebuf, buflen, NULL); - if (result != USB_STOR_XFER_GOOD) { - US_DEBUGP("request sense bulk in failed\n"); - return USB_STOR_TRANSPORT_ERROR; - } else { - US_DEBUGP("request sense worked\n"); - return USB_STOR_TRANSPORT_GOOD; - } + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } /* @@ -369,7 +376,7 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_read2%d %d\n", x, result); return result; @@ -381,9 +388,9 @@ sddr09_readX(struct us_data *us, int x, unsigned long fromaddress, if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_read2%d %d\n", x, result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } /* @@ -497,7 +504,7 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) { result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) US_DEBUGP("Result for send_control in sddr09_erase %d\n", result); @@ -555,7 +562,7 @@ sddr09_writeX(struct us_data *us, result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_writeX %d\n", result); return result; @@ -567,9 +574,9 @@ sddr09_writeX(struct us_data *us, if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_writeX %d\n", result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } /* erase address, write same address */ @@ -633,7 +640,7 @@ sddr09_read_sg_test_only(struct us_data *us) { result = sddr09_send_scsi_command(us, command, 4*nsg+3); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result for send_control in sddr09_read_sg %d\n", result); return result; @@ -641,7 +648,7 @@ sddr09_read_sg_test_only(struct us_data *us) { buf = (unsigned char *) kmalloc(bulklen, GFP_NOIO); if (!buf) - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, buf, bulklen, NULL); @@ -649,10 +656,10 @@ sddr09_read_sg_test_only(struct us_data *us) { if (result != USB_STOR_XFER_GOOD) { US_DEBUGP("Result for bulk_transfer in sddr09_read_sg %d\n", result); - return USB_STOR_TRANSPORT_ERROR; + return -EIO; } - return USB_STOR_TRANSPORT_GOOD; + return 0; } #endif @@ -681,14 +688,13 @@ sddr09_read_status(struct us_data *us, unsigned char *status) { command[1] = LUNBITS; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, data, 64, NULL); *status = data[0]; - return (result == USB_STOR_XFER_GOOD ? - USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } static int @@ -703,6 +709,13 @@ sddr09_read_data(struct us_data *us, unsigned int len, index, offset; int result; + // Figure out the initial LBA and page + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + // Since we only read in one block 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. @@ -711,18 +724,13 @@ sddr09_read_data(struct us_data *us, buffer = kmalloc(len, GFP_NOIO); if (buffer == NULL) { printk("sddr09_read_data: Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } - // Figure out the initial LBA and page - lba = address >> info->blockshift; - page = (address & info->blockmask); - maxlba = info->capacity >> (info->pageshift + info->blockshift); - // This could be made much more efficient by checking for // contiguous LBA's. Another exercise left to the student. - result = USB_STOR_TRANSPORT_GOOD; + result = 0; index = offset = 0; while (sectors > 0) { @@ -735,7 +743,7 @@ sddr09_read_data(struct us_data *us, if (lba >= maxlba) { US_DEBUGP("Error: Requested lba %u exceeds " "maximum %u\n", lba, maxlba); - result = USB_STOR_TRANSPORT_ERROR; + result = -EIO; break; } @@ -749,7 +757,7 @@ sddr09_read_data(struct us_data *us, /* This is not really an error. It just means that the block has never been written. - Instead of returning USB_STOR_TRANSPORT_ERROR + Instead of returning an error it is better to return all zero data. */ memset(buffer, 0, len); @@ -764,7 +772,7 @@ sddr09_read_data(struct us_data *us, result = sddr09_read20(us, address>>1, pages, info->pageshift, buffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) break; } @@ -830,7 +838,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, pba = sddr09_find_unused_pba(info, lba); if (!pba) { printk("sddr09_write_lba: Out of unused blocks\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOSPC; } info->pba_to_lba[pba] = lba; info->lba_to_pba[lba] = pba; @@ -841,7 +849,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, /* Maybe it is impossible to write to PBA 1. Fake success, but don't do anything. */ printk("sddr09: avoid writing to pba 1\n"); - return USB_STOR_TRANSPORT_GOOD; + return 0; } pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT); @@ -850,7 +858,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, address = (pba << (info->pageshift + info->blockshift)); result = sddr09_read22(us, address>>1, info->blocksize, info->pageshift, blockbuffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; /* check old contents and fill lba */ @@ -897,7 +905,7 @@ sddr09_write_lba(struct us_data *us, unsigned int lba, { unsigned char status = 0; int result2 = sddr09_read_status(us, &status); - if (result2 != USB_STOR_TRANSPORT_GOOD) + if (result2) US_DEBUGP("sddr09_write_inplace: cannot read status\n"); else if (status != 0xc0) US_DEBUGP("sddr09_write_inplace: status after write: 0x%x\n", @@ -920,13 +928,20 @@ sddr09_write_data(struct us_data *us, unsigned int sectors) { struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra; - unsigned int lba, page, pages; + unsigned int lba, maxlba, page, pages; unsigned int pagelen, blocklen; unsigned char *blockbuffer; unsigned char *buffer; unsigned int len, index, offset; int result; + // Figure out the initial LBA and page + lba = address >> info->blockshift; + page = (address & info->blockmask); + maxlba = info->capacity >> (info->pageshift + info->blockshift); + if (lba >= maxlba) + return -EIO; + // blockbuffer is used for reading in the old data, overwriting // with the new data, and performing ECC calculations @@ -938,7 +953,7 @@ sddr09_write_data(struct us_data *us, blockbuffer = kmalloc(blocklen, GFP_NOIO); if (!blockbuffer) { printk("sddr09_write_data: Out of memory\n"); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } // Since we don't write the user data directly to the device, @@ -950,14 +965,10 @@ sddr09_write_data(struct us_data *us, if (buffer == NULL) { printk("sddr09_write_data: Out of memory\n"); kfree(blockbuffer); - return USB_STOR_TRANSPORT_ERROR; + return -ENOMEM; } - // Figure out the initial LBA and page - lba = address >> info->blockshift; - page = (address & info->blockmask); - - result = USB_STOR_TRANSPORT_GOOD; + result = 0; index = offset = 0; while (sectors > 0) { @@ -967,13 +978,21 @@ sddr09_write_data(struct us_data *us, pages = min(sectors, info->blocksize - page); len = (pages << info->pageshift); + /* Not overflowing capacity? */ + if (lba >= maxlba) { + US_DEBUGP("Error: Requested lba %u exceeds " + "maximum %u\n", lba, maxlba); + result = -EIO; + break; + } + // Get the data from the transfer buffer usb_stor_access_xfer_buf(buffer, len, us->srb, &index, &offset, FROM_XFER_BUF); result = sddr09_write_lba(us, lba, page, pages, buffer, blockbuffer); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) break; page = 0; @@ -1022,7 +1041,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) { command[1] = LUNBITS; result = sddr09_send_scsi_command(us, command, 12); - if (result != USB_STOR_TRANSPORT_GOOD) + if (result) return result; result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, @@ -1031,8 +1050,7 @@ sddr09_read_deviceID(struct us_data *us, unsigned char *deviceID) { for (i = 0; i < 4; i++) deviceID[i] = content[i]; - return (result == USB_STOR_XFER_GOOD ? - USB_STOR_TRANSPORT_GOOD : USB_STOR_TRANSPORT_ERROR); + return (result == USB_STOR_XFER_GOOD ? 0 : -EIO); } static int @@ -1041,7 +1059,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { unsigned char status; result = sddr09_read_status(us, &status); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_get_wp: read_status fails\n"); return result; } @@ -1057,7 +1075,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) { if (status & 0x1) US_DEBUGP(" Error"); US_DEBUGP("\n"); - return USB_STOR_TRANSPORT_GOOD; + return 0; } #if 0 @@ -1089,7 +1107,7 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) { result = sddr09_read_deviceID(us, deviceID); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("Result of read_deviceID is %d\n", result); printk("sddr09: could not read card info\n"); return NULL; @@ -1200,7 +1218,7 @@ sddr09_read_map(struct us_data *us) { us, address>>1, min(alloc_blocks, numblocks - i), buffer, 0); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { result = -1; goto done; } @@ -1342,29 +1360,53 @@ sddr09_card_info_destructor(void *extra) { kfree(info->pba_to_lba); } -static void -sddr09_init_card_info(struct us_data *us) { - if (!us->extra) { - us->extra = kmalloc(sizeof(struct sddr09_card_info), GFP_NOIO); - if (us->extra) { - memset(us->extra, 0, sizeof(struct sddr09_card_info)); - us->extra_destructor = sddr09_card_info_destructor; - } +static int +sddr09_common_init(struct us_data *us) { + int result; + + /* set the configuration -- STALL is an acceptable response here */ + if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { + US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev + ->actconfig->desc.bConfigurationValue); + return -EINVAL; + } + + result = usb_reset_configuration(us->pusb_dev); + US_DEBUGP("Result of usb_reset_configuration is %d\n", result); + if (result == -EPIPE) { + US_DEBUGP("-- stall on control interface\n"); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + US_DEBUGP("-- Unknown error. Rejecting device\n"); + return -EINVAL; } + + us->extra = kzalloc(sizeof(struct sddr09_card_info), GFP_NOIO); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = sddr09_card_info_destructor; + + nand_init_ecc(); + return 0; } + /* * This is needed at a very early stage. If this is not listed in the * unusual devices list but called from here then LUN 0 of the combo reader * is not recognized. But I do not know what precisely these calls do. */ int -sddr09_init(struct us_data *us) { +usb_stor_sddr09_dpcm_init(struct us_data *us) { int result; unsigned char *data = us->iobuf; + result = sddr09_common_init(us); + if (result) + return result; + result = sddr09_send_command(us, 0x01, USB_DIR_IN, data, 2); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_init: send_command fails\n"); return result; } @@ -1373,7 +1415,7 @@ sddr09_init(struct us_data *us) { // get 07 02 result = sddr09_send_command(us, 0x08, USB_DIR_IN, data, 2); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_init: 2nd send_command fails\n"); return result; } @@ -1382,7 +1424,7 @@ sddr09_init(struct us_data *us) { // get 07 00 result = sddr09_request_sense(us, data, 18); - if (result == USB_STOR_TRANSPORT_GOOD && data[2] != 0) { + if (result == 0 && data[2] != 0) { int j; for (j=0; j<18; j++) printk(" %02X", data[j]); @@ -1398,7 +1440,7 @@ sddr09_init(struct us_data *us) { // test unit ready - return USB_STOR_TRANSPORT_GOOD; /* not result */ + return 0; /* not result */ } /* @@ -1427,13 +1469,6 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) }; info = (struct sddr09_card_info *)us->extra; - if (!info) { - nand_init_ecc(); - sddr09_init_card_info(us); - info = (struct sddr09_card_info *)us->extra; - if (!info) - return USB_STOR_TRANSPORT_ERROR; - } if (srb->cmnd[0] == REQUEST_SENSE && havefakesense) { /* for a faked command, we have to follow with a faked sense */ @@ -1536,7 +1571,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("READ_10: read page %d pagect %d\n", page, pages); - return sddr09_read_data(us, page, pages); + result = sddr09_read_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); } if (srb->cmnd[0] == WRITE_10) { @@ -1549,7 +1586,9 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("WRITE_10: write page %d pagect %d\n", page, pages); - return sddr09_write_data(us, page, pages); + result = sddr09_write_data(us, page, pages); + return (result == 0 ? USB_STOR_TRANSPORT_GOOD : + USB_STOR_TRANSPORT_ERROR); } /* catch-all for all other commands, except @@ -1575,10 +1614,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("SDDR09: Send control for command %s\n", ptr); result = sddr09_send_scsi_command(us, srb->cmnd, 12); - if (result != USB_STOR_TRANSPORT_GOOD) { + if (result) { US_DEBUGP("sddr09_transport: sddr09_send_scsi_command " "returns %d\n", result); - return result; + return USB_STOR_TRANSPORT_ERROR; } if (srb->request_bufflen == 0) @@ -1606,3 +1645,10 @@ int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_GOOD; } +/* + * Initialization routine for the sddr09 subdriver + */ +int +usb_stor_sddr09_init(struct us_data *us) { + return sddr09_common_init(us); +} diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h index c9d78d6188b..c03089a9ec3 100644 --- a/drivers/usb/storage/sddr09.h +++ b/drivers/usb/storage/sddr09.h @@ -31,18 +31,7 @@ extern int sddr09_transport(struct scsi_cmnd *srb, struct us_data *us); -struct sddr09_card_info { - unsigned long capacity; /* Size of card in bytes */ - int pagesize; /* Size of page in bytes */ - int pageshift; /* log2 of pagesize */ - int blocksize; /* Size of block in pages */ - int blockshift; /* log2 of blocksize */ - int blockmask; /* 2^blockshift - 1 */ - int *lba_to_pba; /* logical to physical map */ - int *pba_to_lba; /* physical to logical map */ - int lbact; /* number of available pages */ - int flags; -#define SDDR09_WP 1 /* write protected */ -}; +extern int usb_stor_sddr09_dpcm_init(struct us_data *us); +extern int usb_stor_sddr09_init(struct us_data *us); #endif diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index 0a362cc781a..633a715850a 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -41,39 +41,8 @@ #ifndef _TRANSPORT_H_ #define _TRANSPORT_H_ -#include <linux/config.h> #include <linux/blkdev.h> -/* Protocols */ - -#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_USBAT -#define US_PR_USBAT 0x80 /* SCM-ATAPI bridge */ -#endif -#ifdef CONFIG_USB_STORAGE_SDDR09 -#define US_PR_EUSB_SDDR09 0x81 /* SCM-SCSI bridge for SDDR-09 */ -#endif -#ifdef CONFIG_USB_STORAGE_SDDR55 -#define US_PR_SDDR55 0x82 /* SDDR-55 (made up) */ -#endif -#define US_PR_DPCM_USB 0xf0 /* Combination CB/SDDR09 */ - -#ifdef CONFIG_USB_STORAGE_FREECOM -#define US_PR_FREECOM 0xf1 /* Freecom */ -#endif - -#ifdef CONFIG_USB_STORAGE_DATAFAB -#define US_PR_DATAFAB 0xf2 /* Datafab chipsets */ -#endif - -#ifdef CONFIG_USB_STORAGE_JUMPSHOT -#define US_PR_JUMPSHOT 0xf3 /* Lexar Jumpshot */ -#endif - -#define US_PR_DEVICE 0xff /* Use device's value */ - /* * Bulk only data structures */ diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index f5f47a34b16..dc301e567cf 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -79,13 +79,6 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_USBAT, init_usbat, 0), #endif -/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */ -UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, - "VIA Technologies Inc.", - "USB 2.0 Card Reader", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_RESIDUE ), - /* Reported by Sebastian Kapfer <sebastian_kapfer@gmx.net> * and Olaf Hering <olh@suse.de> (different bcd's, same vendor/product) * for USB floppies that need the SINGLE_LUN enforcement. @@ -96,6 +89,13 @@ UNUSUAL_DEV( 0x0409, 0x0040, 0x0000, 0x9999, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN ), +/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */ +UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, + "VIA Technologies Inc.", + "USB 2.0 Card Reader", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au> * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -187,6 +187,14 @@ UNUSUAL_DEV( 0x04b0, 0x0405, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Patch for Nikon coolpix 2000 + * Submitted by Fabien Cosse <fabien.cosse@wanadoo.fr>*/ +UNUSUAL_DEV( 0x04b0, 0x0301, 0x0010, 0x0010, + "NIKON", + "NIKON DSC E2000", + US_SC_DEVICE, US_PR_DEVICE,NULL, + US_FL_NOT_LOCKABLE ), + /* BENQ DC5330 * Reported by Manuel Fombuena <mfombuena@ya.com> and * Frank Copeland <fjc@thingy.apana.org.au> */ @@ -276,14 +284,14 @@ UNUSUAL_DEV( 0x04e6, 0x0002, 0x0100, 0x0100, UNUSUAL_DEV( 0x04e6, 0x0003, 0x0000, 0x9999, "Sandisk", "ImageMate SDDR09", - US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, - US_FL_SINGLE_LUN ), + US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), /* This entry is from Andries.Brouwer@cwi.nl */ UNUSUAL_DEV( 0x04e6, 0x0005, 0x0100, 0x0208, "SCM Microsystems", "eUSB SmartMedia / CompactFlash Adapter", - US_SC_SCSI, US_PR_DPCM_USB, sddr09_init, + US_SC_SCSI, US_PR_DPCM_USB, usb_stor_sddr09_dpcm_init, 0), #endif @@ -527,6 +535,13 @@ UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999, "Silicon Media R/W", US_SC_DEVICE, US_PR_DEVICE, NULL, 0), +#ifdef CONFIG_USB_STORAGE_ALAUDA +UNUSUAL_DEV( 0x0584, 0x0008, 0x0102, 0x0102, + "Fujifilm", + "DPC-R1 (Alauda)", + US_SC_SCSI, US_PR_ALAUDA, init_alauda, 0 ), +#endif + /* Fabrizio Fellini <fello@libero.it> */ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, "Fujifilm", @@ -673,8 +688,8 @@ UNUSUAL_DEV( 0x0644, 0x0000, 0x0100, 0x0100, UNUSUAL_DEV( 0x066b, 0x0105, 0x0100, 0x0100, "Olympus", "Camedia MAUSB-2", - US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, - US_FL_SINGLE_LUN ), + US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), #endif /* Reported by Darsen Lu <darsen@micro.ee.nthu.edu.tw> */ @@ -739,8 +754,8 @@ UNUSUAL_DEV( 0x0781, 0x0100, 0x0100, 0x0100, UNUSUAL_DEV( 0x0781, 0x0200, 0x0000, 0x9999, "Sandisk", "ImageMate SDDR-09", - US_SC_SCSI, US_PR_EUSB_SDDR09, NULL, - US_FL_SINGLE_LUN ), + US_SC_SCSI, US_PR_EUSB_SDDR09, usb_stor_sddr09_init, + 0), #endif #ifdef CONFIG_USB_STORAGE_FREECOM @@ -776,6 +791,13 @@ UNUSUAL_DEV( 0x07af, 0x0006, 0x0100, 0x0100, US_SC_SCSI, US_PR_DPCM_USB, NULL, 0 ), #endif +#ifdef CONFIG_USB_STORAGE_ALAUDA +UNUSUAL_DEV( 0x07b4, 0x010a, 0x0102, 0x0102, + "Olympus", + "MAUSB-10 (Alauda)", + US_SC_SCSI, US_PR_ALAUDA, init_alauda, 0 ), +#endif + #ifdef CONFIG_USB_STORAGE_DATAFAB UNUSUAL_DEV( 0x07c4, 0xa000, 0x0000, 0x0015, "Datafab", @@ -1134,3 +1156,27 @@ UNUSUAL_DEV( 0x55aa, 0xa103, 0x0000, 0x9999, US_SC_SCSI, US_PR_SDDR55, NULL, US_FL_SINGLE_LUN), #endif + +/* Control/Bulk transport for all SubClass values */ +USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8020, US_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_QIC, US_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_UFI, US_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8070, US_PR_CB, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_SCSI, US_PR_CB, USB_US_TYPE_STOR), + +/* Control/Bulk/Interrupt transport for all SubClass values */ +USUAL_DEV(US_SC_RBC, US_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8020, US_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_QIC, US_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_UFI, US_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8070, US_PR_CBI, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_SCSI, US_PR_CBI, USB_US_TYPE_STOR), + +/* Bulk-only transport for all SubClass values */ +USUAL_DEV(US_SC_RBC, US_PR_BULK, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8020, US_PR_BULK, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_QIC, US_PR_BULK, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_UFI, US_PR_BULK, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_8070, US_PR_BULK, USB_US_TYPE_STOR), +USUAL_DEV(US_SC_SCSI, US_PR_BULK, 0), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 3847ebed2aa..dbcf23980ff 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -94,6 +94,9 @@ #ifdef CONFIG_USB_STORAGE_ONETOUCH #include "onetouch.h" #endif +#ifdef CONFIG_USB_STORAGE_ALAUDA +#include "alauda.h" +#endif /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); @@ -112,49 +115,33 @@ static atomic_t total_threads = ATOMIC_INIT(0); static DECLARE_COMPLETION(threads_gone); -/* The entries in this table, except for final ones here - * (USB_MASS_STORAGE_CLASS and the empty entry), correspond, - * line for line with the entries of us_unsuaul_dev_list[]. +/* + * The entries in this table correspond, line for line, + * with the entries of us_unusual_dev_list[]. */ +#ifndef CONFIG_USB_LIBUSUAL #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ vendorName, productName,useProtocol, useTransport, \ initFunction, flags) \ -{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax) } +{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \ + .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } + +#define USUAL_DEV(useProto, useTrans, useType) \ +{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \ + .driver_info = (USB_US_TYPE_STOR<<24) } static struct usb_device_id storage_usb_ids [] = { # include "unusual_devs.h" #undef UNUSUAL_DEV - /* Control/Bulk transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) }, - - /* Control/Bulk/Interrupt transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) }, - - /* Bulk-only transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) }, - { 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) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, - +#undef USUAL_DEV /* Terminating entry */ { } }; MODULE_DEVICE_TABLE (usb, storage_usb_ids); +#endif /* CONFIG_USB_LIBUSUAL */ /* This is the list of devices we recognize, along with their flag data */ @@ -167,7 +154,6 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); * are free to use as many characters as you like. */ -#undef UNUSUAL_DEV #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ vendor_name, product_name, use_protocol, use_transport, \ init_function, Flags) \ @@ -177,53 +163,18 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); .useProtocol = use_protocol, \ .useTransport = use_transport, \ .initFunction = init_function, \ - .flags = Flags, \ +} + +#define USUAL_DEV(use_protocol, use_transport, use_type) \ +{ \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ } static struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" # undef UNUSUAL_DEV - /* Control/Bulk transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_CB}, - - /* Control/Bulk/Interrupt transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_CBI}, - - /* Bulk-only transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_BULK}, +# undef USUAL_DEV /* Terminating entry */ { NULL } @@ -240,6 +191,8 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message) down(&us->dev_semaphore); US_DEBUGP("%s\n", __FUNCTION__); + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_SUSPEND); iface->dev.power.power_state.event = message.event; /* When runtime PM is working, we'll set a flag to indicate @@ -256,6 +209,8 @@ static int storage_resume(struct usb_interface *iface) down(&us->dev_semaphore); US_DEBUGP("%s\n", __FUNCTION__); + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_RESUME); iface->dev.power.power_state.event = PM_EVENT_ON; up(&us->dev_semaphore); @@ -484,14 +439,20 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf) return 0; } +/* Find an unusual_dev descriptor (always succeeds in the current code) */ +static struct us_unusual_dev *find_unusual(const struct usb_device_id *id) +{ + const int id_index = id - storage_usb_ids; + return &us_unusual_dev_list[id_index]; +} + /* Get the unusual_devs entries and the string descriptors */ -static void get_device_info(struct us_data *us, int id_index) +static void get_device_info(struct us_data *us, const struct usb_device_id *id) { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = &us->pusb_intf->cur_altsetting->desc; - struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index]; - struct usb_device_id *id = &storage_usb_ids[id_index]; + struct us_unusual_dev *unusual_dev = find_unusual(id); /* Store the entries */ us->unusual_dev = unusual_dev; @@ -501,7 +462,7 @@ static void get_device_info(struct us_data *us, int id_index) us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ? idesc->bInterfaceProtocol : unusual_dev->useTransport; - us->flags = unusual_dev->flags; + us->flags = USB_US_ORIG_FLAGS(id->driver_info); /* * This flag is only needed when we're in high-speed, so let's @@ -516,7 +477,7 @@ static void get_device_info(struct us_data *us, int id_index) * from the unusual_devs.h table. */ if (id->idVendor || id->idProduct) { - static char *msgs[3] = { + static const char *msgs[3] = { "an unneeded SubClass entry", "an unneeded Protocol entry", "unneeded SubClass and Protocol entries"}; @@ -529,7 +490,7 @@ static void get_device_info(struct us_data *us, int id_index) if (unusual_dev->useTransport != US_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; - if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE)) + if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE)) printk(KERN_NOTICE USB_STORAGE "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h\n" @@ -686,6 +647,15 @@ static int get_protocol(struct us_data *us) break; #endif +#ifdef CONFIG_USB_STORAGE_ALAUDA + case US_PR_ALAUDA: + us->transport_name = "Alauda Control/Bulk"; + us->transport = alauda_transport; + us->transport_reset = usb_stor_Bulk_reset; + us->max_lun = 1; + break; +#endif + default: return -EIO; } @@ -921,10 +891,12 @@ static int storage_probe(struct usb_interface *intf, { struct Scsi_Host *host; struct us_data *us; - const int id_index = id - storage_usb_ids; int result; struct task_struct *th; + if (usb_usual_check_type(id, USB_US_TYPE_STOR)) + return -ENXIO; + US_DEBUGP("USB Mass Storage device detected\n"); /* @@ -957,29 +929,7 @@ static int storage_probe(struct usb_interface *intf, * of the match from the usb_device_id table, so we can find the * corresponding entry in the private table. */ - get_device_info(us, id_index); - -#ifdef CONFIG_USB_STORAGE_SDDR09 - if (us->protocol == US_PR_EUSB_SDDR09 || - us->protocol == US_PR_DPCM_USB) { - /* set the configuration -- STALL is an acceptable response here */ - if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { - US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev - ->actconfig->desc.bConfigurationValue); - goto BadDevice; - } - result = usb_reset_configuration(us->pusb_dev); - - US_DEBUGP("Result of usb_reset_configuration is %d\n", result); - if (result == -EPIPE) { - US_DEBUGP("-- stall on control interface\n"); - } else if (result != 0) { - /* it's not a stall, but another error -- time to bail */ - US_DEBUGP("-- Unknown error. Rejecting device\n"); - goto BadDevice; - } - } -#endif + get_device_info(us, id); /* Get the transport, protocol, and pipe settings */ result = get_transport(us); @@ -1044,7 +994,6 @@ static void storage_disconnect(struct usb_interface *intf) ***********************************************************************/ static struct usb_driver usb_storage_driver = { - .owner = THIS_MODULE, .name = "usb-storage", .probe = storage_probe, .disconnect = storage_disconnect, @@ -1062,9 +1011,10 @@ static int __init usb_stor_init(void) /* register the driver, return usb_register return code if error */ retval = usb_register(&usb_storage_driver); - if (retval == 0) + if (retval == 0) { printk(KERN_INFO "USB Mass Storage support registered.\n"); - + usb_usual_set_present(USB_US_TYPE_STOR); + } return retval; } @@ -1088,6 +1038,8 @@ static void __exit usb_stor_exit(void) wait_for_completion(&threads_gone); atomic_dec(&total_threads); } + + usb_usual_clear_present(USB_US_TYPE_STOR); } module_init(usb_stor_init); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 98b09711a73..7259fd1f6b0 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -45,6 +45,7 @@ #define _USB_H_ #include <linux/usb.h> +#include <linux/usb_usual.h> #include <linux/blkdev.h> #include <linux/smp_lock.h> #include <linux/completion.h> @@ -63,38 +64,8 @@ struct us_unusual_dev { __u8 useProtocol; __u8 useTransport; int (*initFunction)(struct us_data *); - unsigned int flags; }; -/* - * Static flag definitions. We use this roundabout technique so that the - * proc_info() routine can automatically display a message for each flag. - */ -#define US_DO_ALL_FLAGS \ - US_FLAG(SINGLE_LUN, 0x00000001) \ - /* allow access to only LUN 0 */ \ - US_FLAG(NEED_OVERRIDE, 0x00000002) \ - /* unusual_devs entry is necessary */ \ - US_FLAG(SCM_MULT_TARG, 0x00000004) \ - /* supports multiple targets */ \ - US_FLAG(FIX_INQUIRY, 0x00000008) \ - /* INQUIRY response needs faking */ \ - US_FLAG(FIX_CAPACITY, 0x00000010) \ - /* READ CAPACITY response too big */ \ - US_FLAG(IGNORE_RESIDUE, 0x00000020) \ - /* reported residue is wrong */ \ - US_FLAG(BULK32, 0x00000040) \ - /* Uses 32-byte CBW length */ \ - US_FLAG(NOT_LOCKABLE, 0x00000080) \ - /* PREVENT/ALLOW not supported */ \ - US_FLAG(GO_SLOW, 0x00000100) \ - /* Need delay after Command phase */ \ - US_FLAG(NO_WP_DETECT, 0x00000200) \ - /* Don't check for write-protect */ \ - -#define US_FLAG(name, value) US_FL_##name = value , -enum { US_DO_ALL_FLAGS }; -#undef US_FLAG /* Dynamic flag definitions: used in set_bit() etc. */ #define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */ @@ -122,7 +93,11 @@ enum { US_DO_ALL_FLAGS }; typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*); typedef int (*trans_reset)(struct us_data*); typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*); -typedef void (*extra_data_destructor)(void *); /* extra data destructor */ +typedef void (*extra_data_destructor)(void *); /* extra data destructor */ +typedef void (*pm_hook)(struct us_data *, int); /* power management hook */ + +#define US_SUSPEND 0 +#define US_RESUME 1 /* we allocate one of these for every device that we remember */ struct us_data { @@ -178,6 +153,9 @@ struct us_data { /* subdriver information */ void *extra; /* Any extra data */ extra_data_destructor extra_destructor;/* extra data destructor */ +#ifdef CONFIG_PM + pm_hook suspend_resume_hook; +#endif }; /* Convert between us_data and the corresponding Scsi_Host */ diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 6c3a53f8f26..5d02f16b7d0 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -39,10 +39,15 @@ MODULE_DEVICE_TABLE (usb, skel_table); /* Get a minor range for your devices from the usb maintainer */ #define USB_SKEL_MINOR_BASE 192 +/* our private defines. if this grows any larger, use your own .h file */ +#define MAX_TRANSFER ( PAGE_SIZE - 512 ) +#define WRITES_IN_FLIGHT 8 + /* Structure to hold all of our device specific stuff */ struct usb_skel { struct usb_device * udev; /* the usb device for this device */ struct usb_interface * interface; /* the interface for this device */ + struct semaphore limit_sem; /* limiting the number of writes in progress */ unsigned char * bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ @@ -152,6 +157,7 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs) /* free up our allocated buffer */ usb_buffer_free(urb->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); + up(&dev->limit_sem); } static ssize_t skel_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) @@ -160,6 +166,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou int retval = 0; struct urb *urb = NULL; char *buf = NULL; + size_t writesize = min(count, (size_t)MAX_TRANSFER); dev = (struct usb_skel *)file->private_data; @@ -167,6 +174,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou if (count == 0) goto exit; + /* limit the number of URBs in flight to stop a user from using up all RAM */ + if (down_interruptible(&dev->limit_sem)) { + retval = -ERESTARTSYS; + goto exit; + } + /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { @@ -174,13 +187,13 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou goto error; } - buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + buf = usb_buffer_alloc(dev->udev, writesize, GFP_KERNEL, &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } - if (copy_from_user(buf, user_buffer, count)) { + if (copy_from_user(buf, user_buffer, writesize)) { retval = -EFAULT; goto error; } @@ -188,7 +201,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), - buf, count, skel_write_bulk_callback, dev); + buf, writesize, skel_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* send the data out the bulk port */ @@ -202,11 +215,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou usb_free_urb(urb); exit: - return count; + return writesize; error: - usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); + usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); + up(&dev->limit_sem); return retval; } @@ -238,13 +252,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i int retval = -ENOMEM; /* allocate memory for our device state and initialize it */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { err("Out of memory"); goto error; } - memset(dev, 0x00, sizeof(*dev)); kref_init(&dev->kref); + sema_init(&dev->limit_sem, WRITES_IN_FLIGHT); dev->udev = usb_get_dev(interface_to_usbdev(interface)); dev->interface = interface; @@ -330,7 +344,6 @@ static void skel_disconnect(struct usb_interface *interface) } static struct usb_driver skel_driver = { - .owner = THIS_MODULE, .name = "skeleton", .probe = skel_probe, .disconnect = skel_disconnect, diff --git a/drivers/w1/dscore.c b/drivers/w1/dscore.c index 15fb250451e..b9146306df4 100644 --- a/drivers/w1/dscore.c +++ b/drivers/w1/dscore.c @@ -52,7 +52,6 @@ static int ds_send_control_cmd(struct ds_device *, u16, u16); static struct usb_driver ds_driver = { - .owner = THIS_MODULE, .name = "DS9490R", .probe = ds_probe, .disconnect = ds_disconnect, |