diff options
Diffstat (limited to 'drivers')
137 files changed, 11597 insertions, 10735 deletions
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index c6f8531f4..41ec3e2f5 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -1473,9 +1473,8 @@ static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller, Command->SegmentCount = Request->nr_segments; Command->BufferHeader = Request->bh; RequestBuffer = Request->buffer; - Request->rq_status = RQ_INACTIVE; blkdev_dequeue_request(Request); - wake_up(&wait_for_request); + blkdev_release_request(Request); if (Command->SegmentCount == 1) { DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox; diff --git a/drivers/block/acsi_slm.c b/drivers/block/acsi_slm.c index 51bb6b1a3..62758a297 100644 --- a/drivers/block/acsi_slm.c +++ b/drivers/block/acsi_slm.c @@ -1009,7 +1009,7 @@ int slm_init( void ) devfs_handle = devfs_mk_dir (NULL, "slm", 3, NULL); devfs_register_series (devfs_handle, "%u", MAX_SLM, DEVFS_FL_DEFAULT, - MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + MAJOR_NR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &slm_fops, NULL); return 0; } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index 40e639830..9bbe50523 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -22,7 +22,7 @@ * driver, you'll probably need the Compaq Array Controller Interface * Specificiation (Document number ECG086/1198) */ -#include <linux/config.h> +#include <linux/config.h> /* CONFIG_PROC_FS */ #include <linux/module.h> #include <linux/version.h> #include <linux/types.h> @@ -44,8 +44,8 @@ #define SMART2_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "Compaq SMART2 Driver (v 1.0.4)" -#define DRIVER_VERSION SMART2_DRIVER_VERSION(1,0,4) +#define DRIVER_NAME "Compaq SMART2 Driver (v 2.4.0)" +#define DRIVER_VERSION SMART2_DRIVER_VERSION(2,4,0) #define MAJOR_NR COMPAQ_SMART2_MAJOR #include <linux/blk.h> #include <linux/blkdev.h> @@ -73,7 +73,7 @@ static int eisa[8] = { 0, 0 ,0 ,0, 0, 0 ,0 ,0 }; * product = Marketing Name for the board * access = Address of the struct of function pointers */ -struct board_type products[] = { +static struct board_type products[] = { { 0x0040110E, "IDA", &smart1_access }, { 0x0140110E, "IDA-2", &smart1_access }, { 0x1040110E, "IAES", &smart1_access }, @@ -87,6 +87,7 @@ struct board_type products[] = { { 0x40400E11, "Integrated Array", &smart4_access }, { 0x40500E11, "Smart Array 4200", &smart4_access }, { 0x40510E11, "Smart Array 4250ES", &smart4_access }, + { 0x40580E11, "Smart Array 431", &smart4_access }, }; static struct hd_struct * ida; @@ -95,7 +96,7 @@ static int * ida_blocksizes; static int * ida_hardsizes; static struct gendisk ida_gendisk[MAX_CTLR]; -struct proc_dir_entry *proc_array = NULL; +static struct proc_dir_entry *proc_array = NULL; /* Debug... */ #define DBG(s) do { s } while(0) @@ -106,7 +107,7 @@ struct proc_dir_entry *proc_array = NULL; /* Debug Extra Paranoid... */ #define DBGPX(s) do { } while(0) -void cpqarray_init(void); +int cpqarray_init(void); static int cpqarray_pci_detect(void); static int cpqarray_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn); static ulong remap_pci_mem(ulong base, ulong size); @@ -312,9 +313,8 @@ EXPORT_NO_SYMBOLS; /* This is a bit of a hack... */ int __init init_module(void) { - cpqarray_init(); - if (nr_ctlr == 0) - return -EIO; + if (cpqarray_init() == 0) /* all the block dev numbers already used */ + return -EIO; /* or no controllers were found */ return 0; } @@ -357,8 +357,9 @@ void cleanup_module(void) /* * This is it. Find all the controllers and register them. I really hate * stealing all these major device numbers. + * returns the number of block devices registered. */ -void __init cpqarray_init(void) +int __init cpqarray_init(void) { void (*request_fns[MAX_CTLR])(request_queue_t *) = { do_ida_request0, do_ida_request1, @@ -367,31 +368,52 @@ void __init cpqarray_init(void) do_ida_request6, do_ida_request7, }; int i,j; + int num_cntlrs_reg = 0; /* detect controllers */ cpqarray_pci_detect(); cpqarray_eisa_detect(); if (nr_ctlr == 0) - return; + return(num_cntlrs_reg); printk(DRIVER_NAME "\n"); printk("Found %d controller(s)\n", nr_ctlr); /* allocate space for disk structs */ ida = kmalloc(sizeof(struct hd_struct)*nr_ctlr*NWD*16, GFP_KERNEL); - if(ida==NULL) - goto bail; - ida_sizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); + { + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + + ida_sizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_sizes==NULL) - goto bail2; + { + kfree(ida); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + ida_blocksizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_blocksizes==NULL) - goto bail3; - ida_hardsizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); + { + kfree(ida); + kfree(ida_sizes); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } + + ida_hardsizes = kmalloc(sizeof(int)*nr_ctlr*NWD*16, GFP_KERNEL); if(ida_hardsizes==NULL) - goto bail4; + { + kfree(ida); + kfree(ida_sizes); + kfree(ida_blocksizes); + printk( KERN_ERR "cpqarray: out of memory"); + return(num_cntlrs_reg); + } memset(ida, 0, sizeof(struct hd_struct)*nr_ctlr*NWD*16); memset(ida_sizes, 0, sizeof(int)*nr_ctlr*NWD*16); @@ -399,74 +421,90 @@ void __init cpqarray_init(void) memset(ida_hardsizes, 0, sizeof(int)*nr_ctlr*NWD*16); memset(ida_gendisk, 0, sizeof(struct gendisk)*MAX_CTLR); - /* + /* * register block devices * Find disks and fill in structs * Get an interrupt, set the Q depth and get into /proc */ for(i=0; i< nr_ctlr; i++) { + /* If this successful it should insure that we are the only */ + /* instance of the driver */ + if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) { + printk(KERN_ERR "cpqarray: Unable to get major number %d for ida\n", + MAJOR_NR+i); + continue; + } + + hba[i]->access.set_intr_mask(hba[i], 0); if (request_irq(hba[i]->intr, do_ida_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) { - printk("Unable to get irq %d for %s\n", + printk(KERN_ERR "cpqarray: Unable to get irq %d for %s\n", hba[i]->intr, hba[i]->devname); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); continue; } - if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) { - printk("Unable to get major number %d for ida\n", - MAJOR_NR+i); - continue; - } - + num_cntlrs_reg++; hba[i]->cmd_pool = (cmdlist_t *)kmalloc( NR_CMDS * sizeof(cmdlist_t), GFP_KERNEL); hba[i]->cmd_pool_bits = (__u32*)kmalloc( ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); - if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL) + if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL) { - int j; + nr_ctlr = i; if(hba[i]->cmd_pool_bits) kfree(hba[i]->cmd_pool_bits); if(hba[i]->cmd_pool) kfree(hba[i]->cmd_pool); - for(j=0;i<i;j++) - { - free_irq(hba[j]->intr, hba[j]); - unregister_blkdev(MAJOR_NR+j, hba[j]->devname); - kfree(hba[j]->cmd_pool_bits); - kfree(hba[j]->cmd_pool); - } free_irq(hba[i]->intr, hba[i]); unregister_blkdev(MAJOR_NR+i, hba[i]->devname); - goto bail5; + num_cntlrs_reg--; + printk( KERN_ERR "cpqarray: out of memory"); + + /* If num_cntlrs_reg == 0, no controllers worked. + * init_module will fail, so clean up global + * memory that clean_module would do. + */ + + if (num_cntlrs_reg == 0) + { + kfree(ida); + kfree(ida_sizes); + kfree(ida_hardsizes); + kfree(ida_blocksizes); + } + return(num_cntlrs_reg); + } memset(hba[i]->cmd_pool, 0, NR_CMDS * sizeof(cmdlist_t)); memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+31)/32)*sizeof(__u32)); - printk("Finding drives on %s", hba[i]->devname); + printk(KERN_INFO "cpqarray: Finding drives on %s", + hba[i]->devname); getgeometry(i); start_fwbk(i); hba[i]->access.set_intr_mask(hba[i], FIFO_NOT_EMPTY); + ida_procinit(i); - - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), request_fns[i]); - blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR + i), + request_fns[i]); + blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR + i), 0); blksize_size[MAJOR_NR+i] = ida_blocksizes + (i*256); hardsect_size[MAJOR_NR+i] = ida_hardsizes + (i*256); - read_ahead[MAJOR_NR+i] = READ_AHEAD; + ida_gendisk[i].major = MAJOR_NR + i; ida_gendisk[i].major_name = "ida"; ida_gendisk[i].minor_shift = NWD_SHIFT; ida_gendisk[i].max_p = 16; ida_gendisk[i].part = ida + (i*256); ida_gendisk[i].sizes = ida_sizes + (i*256); - /* ida_gendisk[i].nr_real is handled by getgeometry */ - + ida_gendisk[i].nr_real = 0; + /* Get on the disk list */ ida_gendisk[i].next = gendisk_head; gendisk_head = &ida_gendisk[i]; @@ -479,21 +517,13 @@ void __init cpqarray_init(void) ida_geninit(i); for(j=0; j<NWD; j++) - register_disk(&ida_gendisk[i], MKDEV(MAJOR_NR+i,j<<4), - 16, &ida_fops, hba[i]->drv[j].nr_blks); + register_disk(&ida_gendisk[i], + MKDEV(MAJOR_NR+i,j<<4), + 16, &ida_fops, hba[i]->drv[j].nr_blks); + } /* done ! */ - return; -bail5: - kfree(ida_hardsizes); -bail4: - kfree(ida_blocksizes); -bail3: - kfree(ida_sizes); -bail2: - kfree(ida); -bail: - printk(KERN_ERR "cpqarray: out of memory.\n"); + return(num_cntlrs_reg); } /* @@ -506,103 +536,68 @@ static int cpqarray_pci_detect(void) { int index; unchar bus=0, dev_fn=0; - - /* This seems dumb, surely we could use an array of types to match ?? */ - - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_DEC, - PCI_DEVICE_ID_COMPAQ_42XX, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "42XX Device has been found at %x %x\n", - bus, dev_fn); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - } - - for(index=0; ; index++) { - unsigned short subvendor=0; - if (pcibios_find_device(PCI_VENDOR_ID_NCR, - PCI_DEVICE_ID_NCR_53C1510, index, &bus, &dev_fn)) - break; - printk(KERN_DEBUG "Integrated RAID Chip has been found at %x %x\n", - bus, dev_fn); - if(pcibios_read_config_word(bus, dev_fn, - PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) - { - printk(KERN_DEBUG "cpqarray failed to read subvendor\n"); - break; - } - if(subvendor != PCI_VENDOR_ID_COMPAQ) - break; - printk(KERN_DEBUG "Its a compaq RAID Chip\n"); - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; - } +#define IDA_BOARD_TYPES 3 + static int ida_vendor_id[IDA_BOARD_TYPES] = { PCI_VENDOR_ID_DEC, + PCI_VENDOR_ID_NCR, PCI_VENDOR_ID_COMPAQ }; + static int ida_device_id[IDA_BOARD_TYPES] = { PCI_DEVICE_ID_COMPAQ_42XX, PCI_DEVICE_ID_NCR_53C1510, PCI_DEVICE_ID_COMPAQ_SMART2P }; + int brdtype; + + /* search for all PCI board types that could be for this driver */ + for(brdtype=0; brdtype<IDA_BOARD_TYPES; brdtype++) + { + for(index=0; ; index++) { + if (pcibios_find_device(ida_vendor_id[brdtype], + ida_device_id[brdtype], index, &bus, &dev_fn)) + break; + printk(KERN_DEBUG "cpqarray: Device %x has been found at %x %x\n", + ida_vendor_id[brdtype], bus, dev_fn); + if (index == 1000000) break; + if (nr_ctlr == 8) { + printk(KERN_WARNING "cpqarray: This driver" + " supports a maximum of 8 controllers.\n"); + break; + } - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - /* DOESNT THIS LEAK MEMORY ?????? - AC */ - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; - } +/* if it is a PCI_DEVICE_ID_NCR_53C1510, make sure it's the Compaq version of the chip */ + + if (ida_device_id[brdtype] == PCI_DEVICE_ID_NCR_53C1510) { + unsigned short subvendor=0; + if(pcibios_read_config_word(bus, dev_fn, + PCI_SUBSYSTEM_VENDOR_ID, &subvendor)) + { + printk(KERN_DEBUG "cpqarray: failed to read subvendor\n"); + continue; + } + if(subvendor != PCI_VENDOR_ID_COMPAQ) + { + printk(KERN_DEBUG + "cpqarray: not a Compaq integrated array controller\n"); + continue; + } + } - for(index=0; ; index++) { - if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ, - PCI_DEVICE_ID_COMPAQ_SMART2P, index, &bus, &dev_fn)) - break; + hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); if(hba[nr_ctlr]==NULL) + { + printk(KERN_ERR "cpqarray: out of memory.\n"); + continue; + } + memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); + if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) + { + kfree(hba[nr_ctlr]); + continue; + } + sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); + hba[nr_ctlr]->ctlr = nr_ctlr; + nr_ctlr++; - if (index == 1000000) break; - if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); - break; } - - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); - if(hba[nr_ctlr]==NULL) - { - printk(KERN_ERR "cpqarray: out of memory.\n"); - continue; - } - memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); - if (cpqarray_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) - continue; - sprintf(hba[nr_ctlr]->devname, "ida%d", nr_ctlr); - hba[nr_ctlr]->ctlr = nr_ctlr; - nr_ctlr++; } return nr_ctlr; } + /* * Find the IO address of the controller, its IRQ and so forth. Fill * in some basic stuff into the ctlr_info_t structure. @@ -671,8 +666,9 @@ DBGINFO( } } if (i == NR_PRODUCTS) { - printk("Sorry, I don't know how to access the SMART Array" - " controller %08lx\n", (unsigned long)board_id); + printk(KERN_WARNING "cpqarray: Sorry, I don't know how" + " to access the SMART Array controller %08lx\n", + (unsigned long)board_id); return -1; } @@ -734,8 +730,8 @@ static int cpqarray_eisa_detect(void) while(i<8 && eisa[i]) { if (nr_ctlr == 8) { - printk("This driver supports a maximum of " - "8 controllers.\n"); + printk(KERN_WARNING "cpqarray: This driver supports" + " a maximum of 8 controllers.\n"); break; } board_id = inl(eisa[i]+0xC80); @@ -744,11 +740,11 @@ static int cpqarray_eisa_detect(void) break; if (j == NR_PRODUCTS) { - printk("Sorry, I don't know how to access the SMART" - " Array controller %08lx\n", (unsigned long)board_id); + printk(KERN_WARNING "cpqarray: Sorry, I don't know how" + " to access the SMART Array controller %08lx\n", (unsigned long)board_id); continue; } - hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + hba[nr_ctlr] = (ctlr_info_t *) kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); if(hba[nr_ctlr]==NULL) { printk(KERN_ERR "cpqarray: out of memory.\n"); @@ -885,21 +881,34 @@ static void do_ida_request(int ctlr) queue_head = &blk_dev[MAJOR_NR+ctlr].request_queue.queue_head; if (list_empty(queue_head)) - goto doreq_done; + { + start_io(h); + return; + } + creq = blkdev_entry_next_request(queue_head); if (creq->rq_status == RQ_INACTIVE) - goto doreq_done; + { + start_io(h); + return; + } + if (ctlr != MAJOR(creq->rq_dev)-MAJOR_NR || - ctlr > nr_ctlr || h == NULL) { + ctlr > nr_ctlr || h == NULL) + { printk("doreq cmd for %d, %x at %p\n", ctlr, creq->rq_dev, creq); complete_buffers(creq->bh, 0); - goto doreq_done; + start_io(h); + return; } if ((c = cmd_alloc(h)) == NULL) - goto doreq_done; + { + start_io(h); + return; + } bh = creq->bh; @@ -972,9 +981,9 @@ DBGPX( printk("Done with %p\n", creq); ); /* Put the request on the tail of the request queue */ addQ(&h->reqQ, c); h->Qdepth++; - if (h->Qdepth > h->maxQsinceinit) h->maxQsinceinit = h->Qdepth; + if (h->Qdepth > h->maxQsinceinit) + h->maxQsinceinit = h->Qdepth; -doreq_done: start_io(h); } @@ -1022,28 +1031,24 @@ static inline void complete_buffers(struct buffer_head *bh, int ok) */ static inline void complete_command(cmdlist_t *cmd, int timeout) { - char buf[80]; int ok=1; if (cmd->req.hdr.rcode & RCODE_NONFATAL && (hba[cmd->ctlr]->misc_tflags & MISC_NONFATAL_WARN) == 0) { - sprintf(buf, "Non Fatal error on ida/c%dd%d\n", + printk(KERN_WARNING "Non Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); - console_print(buf); hba[cmd->ctlr]->misc_tflags |= MISC_NONFATAL_WARN; } if (cmd->req.hdr.rcode & RCODE_FATAL) { - sprintf(buf, "Fatal error on ida/c%dd%d\n", + printk(KERN_WARNING "Fatal error on ida/c%dd%d\n", cmd->ctlr, cmd->hdr.unit); - console_print(buf); ok = 0; } if (cmd->req.hdr.rcode & RCODE_INVREQ) { - sprintf(buf, "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", + printk(KERN_WARNING "Invalid request on ida/c%dd%d = (cmd=%x sect=%d cnt=%d sg=%d ret=%x)\n", cmd->ctlr, cmd->hdr.unit, cmd->req.hdr.cmd, cmd->req.hdr.blk, cmd->req.hdr.blk_cnt, cmd->req.hdr.sg_cnt, cmd->req.hdr.rcode); - console_print(buf); ok = 0; } if (timeout) ok = 0; @@ -1077,10 +1082,15 @@ static void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs) if (istat & FIFO_NOT_EMPTY) { while((a = h->access.command_completed(h))) { a1 = a; a &= ~3; - if ((c = h->cmpQ) == NULL) goto bad_completion; + if ((c = h->cmpQ) == NULL) + { + printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1); + continue; + } while(c->busaddr != a) { c = c->next; - if (c == h->cmpQ) break; + if (c == h->cmpQ) + break; } /* * If we've found the command, take it off the @@ -1096,8 +1106,6 @@ static void do_ida_intr(int irq, void *dev_id, struct pt_regs *regs) } continue; } -bad_completion: - printk("Completion of %08lx ignored\n", (unsigned long)a1); } } @@ -1227,16 +1235,27 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) switch(io->cmd) { case PASSTHRU_A: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); - c->req.bp = virt_to_bus(&(io->c)); + c->req.hdr.blk = virt_to_bus(&(io->c)); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; break; case IDA_READ: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } + c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); c->req.hdr.sg_cnt = 1; @@ -1245,7 +1264,12 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) case IDA_WRITE_MEDIA: case DIAG_PASS_THRU: p = kmalloc(io->sg[0].size, GFP_KERNEL); - if (!p) { error = -ENOMEM; goto ioctl_err_exit; } + if (!p) + { + error = -ENOMEM; + cmd_free(NULL, c); + return(error); + } copy_from_user(p, (void*)io->sg[0].addr, io->sg[0].size); c->req.sg[0].size = io->sg[0].size; c->req.sg[0].addr = virt_to_bus(p); @@ -1284,10 +1308,8 @@ static int ida_ctlr_ioctl(int ctlr, int dsk, ida_ioctl_t *io) } io->rcode = c->req.hdr.rcode; - error = 0; -ioctl_err_exit: cmd_free(NULL, c); - return error; + return(0); } /* @@ -1390,8 +1412,8 @@ static int sendcmd( } udelay(10); DBG( - printk("ida%d: idaSendPciCmd FIFO full, waiting!\n", - ctlr); + printk(KERN_WARNING "cpqarray ida%d: idaSendPciCmd FIFO full," + " waiting!\n", ctlr); ); } /* @@ -1401,16 +1423,16 @@ DBG( complete = pollcomplete(ctlr); if (complete != 1) { if (complete != c->busaddr) { - printk( - "ida%d: idaSendPciCmd " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd " "Invalid command list address returned! (%08lx)\n", ctlr, (unsigned long)complete); cmd_free(info_p, c); return (IO_ERROR); } } else { - printk( - "ida%d: idaSendPciCmd Timeout out, " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd Timeout out, " "No command list address returned!\n", ctlr); cmd_free(info_p, c); @@ -1419,9 +1441,9 @@ DBG( if (c->req.hdr.rcode & 0x00FE) { if (!(c->req.hdr.rcode & BIG_PROBLEM)) { - printk( - "ida%d: idaSendPciCmd, error: Controller failed " - "at init time " + printk( KERN_WARNING + "cpqarray ida%d: idaSendPciCmd, error: " + "Controller failed at init time " "cmd: 0x%x, return code = 0x%x\n", ctlr, c->req.hdr.cmd, c->req.hdr.rcode); @@ -1461,8 +1483,8 @@ static int revalidate_allvol(kdev_t dev) spin_lock_irqsave(&io_request_lock, flags); if (hba[ctlr]->usage_count > 1) { spin_unlock_irqrestore(&io_request_lock, flags); - printk("Device busy for volume revalidation (usage=%d)\n", - hba[ctlr]->usage_count); + printk(KERN_WARNING "cpqarray: Device busy for volume" + " revalidation (usage=%d)\n", hba[ctlr]->usage_count); return -EBUSY; } spin_unlock_irqrestore(&io_request_lock, flags); @@ -1514,8 +1536,9 @@ static int revalidate_logvol(kdev_t dev, int maxusage) spin_lock_irqsave(&io_request_lock, flags); if (hba[ctlr]->drv[target].usage_count > maxusage) { spin_unlock_irqrestore(&io_request_lock, flags); - printk("Device busy for revalidation (usage=%d)\n", - hba[ctlr]->drv[target].usage_count); + printk(KERN_WARNING "cpqarray: Device busy for " + "revalidation (usage=%d)\n", + hba[ctlr]->drv[target].usage_count); return -EBUSY; } @@ -1581,22 +1604,28 @@ static void start_fwbk(int ctlr) id_ctlr_t *id_ctlr_buf; int ret_code; - if( hba[ctlr]->board_id != 0x40400E11) + if( (hba[ctlr]->board_id != 0x40400E11) + && (hba[ctlr]->board_id != 0x40480E11) ) + /* Not a Integrated Raid, so there is nothing for us to do */ return; - printk(KERN_DEBUG "Starting firmware's background processing\n"); + printk(KERN_DEBUG "cpqarray: Starting firmware's background" + " processing\n"); /* Command does not return anything, but idasend command needs a buffer */ id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL); if(id_ctlr_buf==NULL) { - printk(KERN_WARNING "Out of memory. Unable to start background processing.\n"); + printk(KERN_WARNING "cpqarray: Out of memory. " + "Unable to start background processing.\n"); return; } ret_code = sendcmd(RESUME_BACKGROUND_ACTIVITY, ctlr, id_ctlr_buf, 0, 0, 0, 0); if(ret_code != IO_OK) - printk(KERN_WARNING "Unable to start background processing\n"); + printk(KERN_WARNING "cpqarray: Unable to start" + " background processing\n"); + kfree(id_ctlr_buf); } /***************************************************************** @@ -1620,16 +1649,38 @@ static void getgeometry(int ctlr) id_ldrive = (id_log_drv_t *)kmalloc(sizeof(id_log_drv_t), GFP_KERNEL); if(id_ldrive == NULL) + { + printk( KERN_ERR "cpqarray: out of memory.\n"); return; + } + id_ctlr_buf = (id_ctlr_t *)kmalloc(sizeof(id_ctlr_t), GFP_KERNEL); if(id_ctlr_buf == NULL) - goto bail2; + { + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + id_lstatus_buf = (sense_log_drv_stat_t *)kmalloc(sizeof(sense_log_drv_stat_t), GFP_KERNEL); if(id_lstatus_buf == NULL) - goto bail3; + { + kfree(id_ctlr_buf); + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + sense_config_buf = (config_t *)kmalloc(sizeof(config_t), GFP_KERNEL); if(sense_config_buf == NULL) - goto bail4; + { + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + printk( KERN_ERR "cpqarray: out of memory.\n"); + return; + } + memset(id_ldrive, 0, sizeof(id_log_drv_t)); memset(id_ctlr_buf, 0, sizeof(id_ctlr_t)); memset(id_lstatus_buf, 0, sizeof(sense_log_drv_stat_t)); @@ -1648,8 +1699,15 @@ static void getgeometry(int ctlr) * so the idastubopen will fail on all logical drives * on the controller. */ - goto geo_ret; /* release the buf and return */ - } + /* Free all the buffers and return */ + printk(KERN_ERR "cpqarray: error sending ID controller\n"); + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; + } + info_p->log_drives = id_ctlr_buf->nr_drvs;; *(__u32*)(info_p->firm_rev) = *(__u32*)(id_ctlr_buf->firm_rev); info_p->ctlr_sig = id_ctlr_buf->cfg_sig; @@ -1663,8 +1721,9 @@ static void getgeometry(int ctlr) * Get drive geometry for all logical drives */ if (id_ctlr_buf->nr_drvs > 16) - printk("ida%d: This driver supports 16 logical drives " - "per controller.\n. Additional drives will not be " + printk(KERN_WARNING "cpqarray ida%d: This driver supports " + "16 logical drives per controller.\n. " + " Additional drives will not be " "detected\n", ctlr); for (log_unit = 0; @@ -1687,13 +1746,17 @@ static void getgeometry(int ctlr) on the controller. */ info_p->log_drv_map = 0; - printk( - "ida%d: idaGetGeometry - Controller failed " - "to report status of logical drive %d\n" + printk( KERN_WARNING + "cpqarray ida%d: idaGetGeometry - Controller" + " failed to report status of logical drive %d\n" "Access to this controller has been disabled\n", ctlr, log_unit); - goto geo_ret; /* release the buf and return */ - + /* Free all the buffers and return */ + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; } /* Make sure the logical drive is configured @@ -1715,14 +1778,21 @@ static void getgeometry(int ctlr) drv->sectors = id_ldrive->drv.sect_per_track; info_p->log_drv_map |= (1 << log_unit); - printk("ida/c%dd%d: blksz=%d nr_blks=%d\n", + printk(KERN_INFO "cpqarray ida/c%dd%d: blksz=%d nr_blks=%d\n", ctlr, log_unit, drv->blk_size, drv->nr_blks); ret_code = sendcmd(SENSE_CONFIG, ctlr, sense_config_buf, sizeof(config_t), 0, 0, log_unit); if (ret_code == IO_ERROR) { info_p->log_drv_map = 0; - goto geo_ret; /* release the buf and return */ + /* Free all the buffers and return */ + printk(KERN_ERR "cpqarray: error sending sense config\n"); + kfree(sense_config_buf); + kfree(id_lstatus_buf); + kfree(id_ctlr_buf); + kfree(id_ldrive); + return; + } info_p->phys_drives = sense_config_buf->ctlr_phys_drv; @@ -1736,12 +1806,10 @@ static void getgeometry(int ctlr) log_index = log_index + 1; } /* end of if logical drive configured */ } /* end of for log_unit */ -geo_ret: kfree(sense_config_buf); -bail4: - kfree(id_ldrive); -bail3: - kfree(id_lstatus_buf); -bail2: + kfree(id_ldrive); + kfree(id_lstatus_buf); kfree(id_ctlr_buf); + return; + } diff --git a/drivers/block/elevator.c b/drivers/block/elevator.c index 26f02abe3..2bf92251c 100644 --- a/drivers/block/elevator.c +++ b/drivers/block/elevator.c @@ -4,6 +4,16 @@ * Block device elevator/IO-scheduler. * * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE + * + * 30042000 Jens Axboe <axboe@suse.de> : + * + * Split the elevator a bit so that it is possible to choose a different + * one or even write a new "plug in". There are three pieces: + * - elevator_fn, inserts a new request in the queue list + * - elevator_merge_fn, decides whether a new buffer can be merged with + * an existing request + * - elevator_dequeue_fn, called when a request is taken off the active list + * */ #include <linux/fs.h> @@ -12,9 +22,9 @@ #include <linux/blk.h> #include <asm/uaccess.h> -static void elevator_default(struct request * req, elevator_t * elevator, - struct list_head * real_head, - struct list_head * head, int orig_latency) +void elevator_default(struct request *req, elevator_t * elevator, + struct list_head * real_head, + struct list_head * head, int orig_latency) { struct list_head * entry = real_head, * point = NULL; struct request * tmp; @@ -22,6 +32,12 @@ static void elevator_default(struct request * req, elevator_t * elevator, int latency = orig_latency -= elevator->nr_segments, pass = 0; int point_latency = 0xbeefbeef; + if (list_empty(real_head)) { + req->elevator_sequence = elevator_sequence(elevator, orig_latency); + list_add(&req->queue, real_head); + return; + } + while ((entry = entry->prev) != head) { if (!point && latency >= 0) { point = entry; @@ -49,19 +65,189 @@ static void elevator_default(struct request * req, elevator_t * elevator, req->elevator_sequence = elevator_sequence(elevator, latency); } +int elevator_default_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + elevator_t *elevator = &q->elevator; + int orig_latency, latency, sequence, action, starving = 0; + + /* + * Avoid write-bombs as not to hurt interactiveness of reads + */ + if (rw == WRITE) + *max_segments = elevator->max_bomb_segments; + + latency = orig_latency = elevator_request_latency(elevator, rw); + sequence = elevator->sequence; + + entry = head; + if (q->head_active && !q->plugged) + head = head->next; + + while ((entry = entry->prev) != head && !starving) { + *req = blkdev_entry_to_request(entry); + latency += (*req)->nr_segments; + if (elevator_sequence_before((*req)->elevator_sequence, sequence)) + starving = 1; + if (latency < 0) + continue; + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) { + if (latency - (*req)->nr_segments < 0) + break; + action = ELEVATOR_BACK_MERGE; + } else if ((*req)->sector - count == bh->b_rsector) { + if (starving) + break; + action = ELEVATOR_FRONT_MERGE; + } else { + continue; + } + q->elevator.sequence++; + return action; + } + return ELEVATOR_NO_MERGE; +} + +inline void elevator_default_dequeue(struct request *req) +{ + if (req->cmd == READ) + req->e->read_pendings--; + + req->e->nr_segments -= req->nr_segments; +} + +/* + * Order ascending, but only allow a request to be skipped a certain + * number of times + */ +void elevator_linus(struct request *req, elevator_t *elevator, + struct list_head *real_head, + struct list_head *head, int orig_latency) +{ + struct list_head *entry = real_head; + struct request *tmp; + + if (list_empty(real_head)) { + list_add(&req->queue, real_head); + return; + } + + while ((entry = entry->prev) != head) { + tmp = blkdev_entry_to_request(entry); + if (!tmp->elevator_sequence) + break; + if (IN_ORDER(tmp, req)) + break; + tmp->elevator_sequence--; + } + list_add(&req->queue, entry); +} + +int elevator_linus_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + entry = head; + if (q->head_active && !q->plugged) + head = head->next; + + while ((entry = entry->prev) != head) { + *req = blkdev_entry_to_request(entry); + if (!(*req)->elevator_sequence) + break; + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*req)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + (*req)->elevator_sequence--; + } + return ELEVATOR_NO_MERGE; +} + +/* + * No request sorting, just add it to the back of the list + */ +void elevator_noop(struct request *req, elevator_t *elevator, + struct list_head *real_head, struct list_head *head, + int orig_latency) +{ + list_add_tail(&req->queue, real_head); +} + +/* + * See if we can find a request that is buffer can be coalesced with. + */ +int elevator_noop_merge(request_queue_t *q, struct request **req, + struct buffer_head *bh, int rw, + int *max_sectors, int *max_segments) +{ + struct list_head *entry, *head = &q->queue_head; + unsigned int count = bh->b_size >> 9; + + if (q->head_active && !q->plugged) + head = head->next; + + entry = head; + while ((entry = entry->prev) != head) { + *req = blkdev_entry_to_request(entry); + if ((*req)->sem) + continue; + if ((*req)->cmd != rw) + continue; + if ((*req)->nr_sectors + count > *max_sectors) + continue; + if ((*req)->rq_dev != bh->b_rdev) + continue; + if ((*req)->sector + (*req)->nr_sectors == bh->b_rsector) + return ELEVATOR_BACK_MERGE; + if ((*req)->sector - count == bh->b_rsector) + return ELEVATOR_FRONT_MERGE; + } + return ELEVATOR_NO_MERGE; +} + +/* + * The noop "elevator" does not do any accounting + */ +void elevator_noop_dequeue(struct request *req) {} + #ifdef ELEVATOR_DEBUG -void elevator_debug(request_queue_t * q, kdev_t dev) +void elevator_default_debug(request_queue_t * q, kdev_t dev) { int read_pendings = 0, nr_segments = 0; elevator_t * elevator = &q->elevator; struct list_head * entry = &q->queue_head; static int counter; + if (elevator->elevator_fn != elevator_default) + return; + if (counter++ % 100) return; - while ((entry = entry->prev) != &q->queue_head) - { + while ((entry = entry->prev) != &q->queue_head) { struct request * req; req = blkdev_entry_to_request(entry); @@ -81,16 +267,14 @@ void elevator_debug(request_queue_t * q, kdev_t dev) nr_segments += req->nr_segments; } - if (read_pendings != elevator->read_pendings) - { + if (read_pendings != elevator->read_pendings) { printk(KERN_WARNING "%s: elevator read_pendings %d should be %d\n", kdevname(dev), elevator->read_pendings, read_pendings); elevator->read_pendings = read_pendings; } - if (nr_segments != elevator->nr_segments) - { + if (nr_segments != elevator->nr_segments) { printk(KERN_WARNING "%s: elevator nr_segments %d should be %d\n", kdevname(dev), elevator->nr_segments, @@ -102,7 +286,6 @@ void elevator_debug(request_queue_t * q, kdev_t dev) int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) { - int ret; blkelv_ioctl_arg_t output; output.queue_ID = elevator->queue_ID; @@ -110,44 +293,37 @@ int blkelvget_ioctl(elevator_t * elevator, blkelv_ioctl_arg_t * arg) output.write_latency = elevator->write_latency; output.max_bomb_segments = elevator->max_bomb_segments; - ret = -EFAULT; if (copy_to_user(arg, &output, sizeof(blkelv_ioctl_arg_t))) - goto out; - ret = 0; - out: - return ret; + return -EFAULT; + + return 0; } int blkelvset_ioctl(elevator_t * elevator, const blkelv_ioctl_arg_t * arg) { blkelv_ioctl_arg_t input; - int ret; - ret = -EFAULT; if (copy_from_user(&input, arg, sizeof(blkelv_ioctl_arg_t))) - goto out; + return -EFAULT; - ret = -EINVAL; if (input.read_latency < 0) - goto out; + return -EINVAL; if (input.write_latency < 0) - goto out; + return -EINVAL; if (input.max_bomb_segments <= 0) - goto out; + return -EINVAL; elevator->read_latency = input.read_latency; elevator->write_latency = input.write_latency; elevator->max_bomb_segments = input.max_bomb_segments; - ret = 0; - out: - return ret; + return 0; } -void elevator_init(elevator_t * elevator) +void elevator_init(elevator_t * elevator, elevator_t type) { static unsigned int queue_ID; - *elevator = ELEVATOR_DEFAULTS; + *elevator = type; elevator->queue_ID = queue_ID++; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8f4734818..1fcd8b73f 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3878,10 +3878,10 @@ static void __init register_devfs_entries (int drive) char name[16]; sprintf (name, "%d%s", drive, table[table_sup[UDP->cmos][i]]); - devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, MAJOR_NR, + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, MAJOR_NR, base_minor + (table_sup[UDP->cmos][i] << 2), S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP, - 0, 0, &floppy_fops, NULL); + &floppy_fops, NULL); } while (table_sup[UDP->cmos][i++]); } } diff --git a/drivers/block/ida_cmd.h b/drivers/block/ida_cmd.h index 056863b60..6d57a85ee 100644 --- a/drivers/block/ida_cmd.h +++ b/drivers/block/ida_cmd.h @@ -191,7 +191,7 @@ typedef struct { __u8 expn_fail; __u8 unit_flags; __u16 big_fail_map[8]; - __u16 big_remap_map[8]; + __u16 big_remap_map[128]; __u16 big_repl_map[8]; __u16 big_act_spare_map[8]; __u8 big_spar_repl_map[128]; @@ -336,7 +336,7 @@ typedef struct { __u32 sense_info; __u8 sense_code; __u8 sense_qual; - __u8 residual; + __u32 residual; __u8 reserved[4]; __u8 cdb[12]; } scsi_param_t; diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 18c7dc1fa..2ed93b300 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -4,6 +4,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994, Karl Keyte: Added support for disk statistics * Elevator latency, (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE + * Queue request tables / lock, selectable elevator, Jens Axboe <axboe@suse.de> */ /* @@ -37,10 +38,9 @@ extern int mac_floppy_init(void); #endif /* - * The request-struct contains all necessary data - * to load a nr of sectors into memory + * For the allocated request tables */ -static struct request all_requests[NR_REQUEST]; +static kmem_cache_t *request_cachep; /* * The "disk" task queue is used to start the actual requests @@ -62,11 +62,6 @@ DECLARE_TASK_QUEUE(tq_disk); */ spinlock_t io_request_lock = SPIN_LOCK_UNLOCKED; -/* - * used to wait on when there are no free requests - */ -DECLARE_WAIT_QUEUE_HEAD(wait_for_request); - /* This specifies how many sectors to read ahead on the disk. */ int read_ahead[MAX_BLKDEV]; @@ -127,29 +122,61 @@ static inline int get_max_sectors(kdev_t dev) return max_sectors[MAJOR(dev)][MINOR(dev)]; } +static inline request_queue_t *__blk_get_queue(kdev_t dev) +{ + struct blk_dev_struct *bdev = blk_dev + MAJOR(dev); + + if (bdev->queue) + return bdev->queue(dev); + else + return &blk_dev[MAJOR(dev)].request_queue; +} + /* * NOTE: the device-specific queue() functions * have to be atomic! */ -request_queue_t * blk_get_queue (kdev_t dev) +request_queue_t *blk_get_queue(kdev_t dev) { - int major = MAJOR(dev); - struct blk_dev_struct *bdev = blk_dev + major; - unsigned long flags; request_queue_t *ret; + unsigned long flags; spin_lock_irqsave(&io_request_lock,flags); - if (bdev->queue) - ret = bdev->queue(dev); - else - ret = &blk_dev[major].request_queue; + ret = __blk_get_queue(dev); spin_unlock_irqrestore(&io_request_lock,flags); return ret; } +/* + * Hopefully the low level driver has finished any out standing requests + * first... + */ void blk_cleanup_queue(request_queue_t * q) { + struct list_head *entry; + struct request *rq; + int i = QUEUE_NR_REQUESTS; + + if (list_empty(&q->request_freelist)) + return; + + if (q->queue_requests) + BUG(); + + entry = &q->request_freelist; + entry = entry->next; + do { + rq = list_entry(entry, struct request, table); + entry = entry->next; + list_del(&rq->table); + kmem_cache_free(request_cachep, rq); + i--; + } while (!list_empty(&q->request_freelist)); + + if (i) + printk("blk_cleanup_queue: leaked requests (%d)\n", i); + memset(q, 0, sizeof(*q)); } @@ -222,7 +249,7 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req, * This is called with interrupts off and no requests on the queue. * (and with the request spinlock aquired) */ -static void generic_plug_device (request_queue_t *q, kdev_t dev) +static void generic_plug_device(request_queue_t *q, kdev_t dev) { #ifdef CONFIG_BLK_DEV_MD if (MAJOR(dev) == MD_MAJOR) { @@ -230,25 +257,51 @@ static void generic_plug_device (request_queue_t *q, kdev_t dev) BUG(); } #endif - if (!list_empty(&q->queue_head)) + /* + * no need to replug device + */ + if (!list_empty(&q->queue_head) || q->plugged) return; q->plugged = 1; queue_task(&q->plug_tq, &tq_disk); } +static void blk_init_free_list(request_queue_t *q) +{ + struct request *rq; + int i; + + /* + * Divide requests in half between read and write. This used to + * be a 2/3 advantage for reads, but now reads can steal from + * the write free list. + */ + for (i = 0; i < QUEUE_NR_REQUESTS; i++) { + rq = kmem_cache_alloc(request_cachep, SLAB_KERNEL); + rq->rq_status = RQ_INACTIVE; + list_add(&rq->table, &q->request_freelist); + } + + q->queue_requests = 0; + init_waitqueue_head(&q->wait_for_request); + spin_lock_init(&q->request_lock); +} + void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) { INIT_LIST_HEAD(&q->queue_head); - elevator_init(&q->elevator); + INIT_LIST_HEAD(&q->request_freelist); + elevator_init(&q->elevator, ELEVATOR_LINUS); + blk_init_free_list(q); q->request_fn = rfn; q->back_merge_fn = ll_back_merge_fn; q->front_merge_fn = ll_front_merge_fn; q->merge_requests_fn = ll_merge_requests_fn; q->make_request_fn = NULL; - q->plug_tq.sync = 0; + q->plug_tq.sync = 0; q->plug_tq.routine = &generic_unplug_device; - q->plug_tq.data = q; + q->plug_tq.data = q; q->plugged = 0; /* * These booleans describe the queue properties. We set the @@ -263,89 +316,88 @@ void blk_init_queue(request_queue_t * q, request_fn_proc * rfn) /* * remove the plug and let it rip.. */ -void generic_unplug_device(void * data) +static inline void __generic_unplug_device(request_queue_t *q) { - request_queue_t * q = (request_queue_t *) data; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock,flags); if (q->plugged) { q->plugged = 0; if (!list_empty(&q->queue_head)) - (q->request_fn)(q); + q->request_fn(q); } - spin_unlock_irqrestore(&io_request_lock,flags); } +void generic_unplug_device(void *data) +{ + request_queue_t *q = (request_queue_t *) data; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + __generic_unplug_device(q); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +#define blkdev_free_rq(list) list_entry((list)->next, struct request, table); /* - * look for a free request in the first N entries. - * NOTE: interrupts must be disabled on the way in (on SMP the request queue - * spinlock has to be aquired), and will still be disabled on the way out. + * Get a free request. io_request_lock must be held and interrupts + * disabled on the way in. */ -static inline struct request * get_request(int n, kdev_t dev) +static inline struct request *get_request(request_queue_t *q, int rw) { - static struct request *prev_found = NULL, *prev_limit = NULL; - register struct request *req, *limit; + register struct request *rq = NULL; - if (n <= 0) - panic("get_request(%d): impossible!\n", n); + if (!list_empty(&q->request_freelist)) { + elevator_t *e = &q->elevator; - limit = all_requests + n; - if (limit != prev_limit) { - prev_limit = limit; - prev_found = all_requests; - } - req = prev_found; - for (;;) { - req = ((req > all_requests) ? req : limit) - 1; - if (req->rq_status == RQ_INACTIVE) - break; - if (req == prev_found) + if ((q->queue_requests > QUEUE_WRITES_MAX) && (rw == WRITE)) return NULL; + + rq = blkdev_free_rq(&q->request_freelist); + list_del(&rq->table); + rq->rq_status = RQ_ACTIVE; + rq->special = NULL; + rq->q = q; + if (rq->cmd == READ) + rq->elevator_sequence = e->read_latency; + else + rq->elevator_sequence = e->write_latency; + q->queue_requests++; } - prev_found = req; - req->rq_status = RQ_ACTIVE; - req->rq_dev = dev; - req->special = NULL; - return req; + return rq; } /* - * wait until a free request in the first N entries is available. + * No available requests for this queue, unplug the device. */ -static struct request * __get_request_wait(int n, kdev_t dev) +static struct request *__get_request_wait(request_queue_t *q, int rw) { - register struct request *req; + register struct request *rq; DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - add_wait_queue_exclusive(&wait_for_request, &wait); + add_wait_queue_exclusive(&q->wait_for_request, &wait); for (;;) { - __set_current_state(TASK_UNINTERRUPTIBLE|TASK_EXCLUSIVE); - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) + __set_current_state(TASK_UNINTERRUPTIBLE | TASK_EXCLUSIVE); + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) break; - run_task_queue(&tq_disk); + generic_unplug_device(q); schedule(); } - remove_wait_queue(&wait_for_request, &wait); + remove_wait_queue(&q->wait_for_request, &wait); current->state = TASK_RUNNING; - return req; + return rq; } -static inline struct request * get_request_wait(int n, kdev_t dev) +static inline struct request *get_request_wait(request_queue_t *q, int rw) { - register struct request *req; - unsigned long flags; - - spin_lock_irqsave(&io_request_lock,flags); - req = get_request(n, dev); - spin_unlock_irqrestore(&io_request_lock,flags); - if (req) - return req; - return __get_request_wait(n, dev); + register struct request *rq; + + spin_lock_irq(&io_request_lock); + rq = get_request(q, rw); + spin_unlock_irq(&io_request_lock); + if (rq) + return rq; + return __get_request_wait(q, rw); } /* RO fail safe mechanism */ @@ -405,36 +457,45 @@ inline void drive_stat_acct (kdev_t dev, int rw, */ static inline void add_request(request_queue_t * q, struct request * req, - struct list_head * head, int latency) + struct list_head *head, int lat) { int major; drive_stat_acct(req->rq_dev, req->cmd, req->nr_sectors, 1); - - elevator_account_request(&q->elevator, req); - if (list_empty(head)) { - req->elevator_sequence = elevator_sequence(&q->elevator, latency); - list_add(&req->queue, &q->queue_head); - return; - } - q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, latency); - + elevator_account_request(req); /* + * let selected elevator insert the request + */ + q->elevator.elevator_fn(req, &q->elevator, &q->queue_head, head, lat); + + /* * FIXME(eric) I don't understand why there is a need for this * special case code. It clearly doesn't fit any more with * the new queueing architecture, and it got added in 2.3.10. * I am leaving this in here until I hear back from the COMPAQ * people. - */ + */ major = MAJOR(req->rq_dev); if (major >= COMPAQ_SMART2_MAJOR+0 && major <= COMPAQ_SMART2_MAJOR+7) - { (q->request_fn)(q); - } - if (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) - { (q->request_fn)(q); +} + +/* + * Must be called with io_request_lock held and interrupts disabled + */ +void inline blkdev_release_request(struct request *req) +{ + req->rq_status = RQ_INACTIVE; + + /* + * Request may not have originated from ll_rw_blk + */ + if (req->q) { + list_add(&req->table, &req->q->request_freelist); + req->q->queue_requests--; + wake_up(&req->q->wait_for_request); } } @@ -462,13 +523,12 @@ static void attempt_merge(request_queue_t * q, if(!(q->merge_requests_fn)(q, req, next, max_segments)) return; - elevator_merge_requests(&q->elevator, req, next); + elevator_merge_requests(req, next); req->bhtail->b_reqnext = next->bh; req->bhtail = next->bhtail; req->nr_sectors = req->hard_nr_sectors += next->hard_nr_sectors; - next->rq_status = RQ_INACTIVE; list_del(&next->queue); - wake_up (&wait_for_request); + blkdev_release_request(next); } static inline void attempt_back_merge(request_queue_t * q, @@ -496,18 +556,16 @@ static inline void attempt_front_merge(request_queue_t * q, } static inline void __make_request(request_queue_t * q, int rw, - struct buffer_head * bh) + struct buffer_head * bh) { int major = MAJOR(bh->b_rdev); unsigned int sector, count; int max_segments = MAX_SEGMENTS; - struct request * req; - int rw_ahead, max_req, max_sectors; - unsigned long flags; - - int orig_latency, latency, starving, sequence; - struct list_head * entry, * head = &q->queue_head; - elevator_t * elevator; + struct request * req = NULL; + int rw_ahead, max_sectors, el_ret; + struct list_head *head = &q->queue_head; + int latency; + elevator_t *elevator = &q->elevator; count = bh->b_size >> 9; sector = bh->b_rsector; @@ -541,7 +599,6 @@ static inline void __make_request(request_queue_t * q, int rw, if (buffer_uptodate(bh)) /* Hmmph! Already have it */ goto end_io; kstat.pgpgin++; - max_req = NR_REQUEST; /* reads take precedence */ break; case WRITERAW: rw = WRITE; @@ -558,7 +615,6 @@ static inline void __make_request(request_queue_t * q, int rw, * requests are only for reads. */ kstat.pgpgout++; - max_req = (NR_REQUEST * 2) / 3; break; default: BUG(); @@ -583,158 +639,82 @@ static inline void __make_request(request_queue_t * q, int rw, /* look for a free request. */ /* - * Loop uses two requests, 1 for loop and 1 for the real device. - * Cut max_req in half to avoid running out and deadlocking. - */ - if ((major == LOOP_MAJOR) || (major == NBD_MAJOR)) - max_req >>= 1; - - /* * Try to coalesce the new request with old requests */ max_sectors = get_max_sectors(bh->b_rdev); - elevator = &q->elevator; - orig_latency = elevator_request_latency(elevator, rw); + latency = elevator_request_latency(elevator, rw); /* * Now we acquire the request spinlock, we have to be mega careful * not to schedule or do something nonatomic */ - spin_lock_irqsave(&io_request_lock,flags); - elevator_debug(q, bh->b_rdev); + spin_lock_irq(&io_request_lock); + elevator_default_debug(q, bh->b_rdev); if (list_empty(head)) { q->plug_device_fn(q, bh->b_rdev); /* is atomic */ goto get_rq; } - /* avoid write-bombs to not hurt iteractiveness of reads */ - if (rw != READ && elevator->read_pendings) - max_segments = elevator->max_bomb_segments; - - sequence = elevator->sequence; - latency = orig_latency - elevator->nr_segments; - starving = 0; - entry = head; + el_ret = elevator->elevator_merge_fn(q, &req, bh, rw, &max_sectors, &max_segments); + switch (el_ret) { - /* - * The scsi disk and cdrom drivers completely remove the request - * from the queue when they start processing an entry. For this - * reason it is safe to continue to add links to the top entry - * for those devices. - * - * All other drivers need to jump over the first entry, as that - * entry may be busy being processed and we thus can't change - * it. - */ - if (q->head_active && !q->plugged) - head = head->next; - - while ((entry = entry->prev) != head && !starving) { - req = blkdev_entry_to_request(entry); - if (!req->q) - break; - latency += req->nr_segments; - if (elevator_sequence_before(req->elevator_sequence, sequence)) - starving = 1; - if (latency < 0) - continue; - - if (req->sem) - continue; - if (req->cmd != rw) - continue; - if (req->nr_sectors + count > max_sectors) - continue; - if (req->rq_dev != bh->b_rdev) - continue; - /* Can we add it to the end of this request? */ - if (req->sector + req->nr_sectors == sector) { - if (latency - req->nr_segments < 0) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->back_merge_fn)(q, req, bh, max_segments)) + case ELEVATOR_BACK_MERGE: + if (!q->back_merge_fn(q, req, bh, max_segments)) break; req->bhtail->b_reqnext = bh; req->bhtail = bh; - req->nr_sectors = req->hard_nr_sectors += count; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_after(elevator, req, latency); - - /* Can we now merge this req with the next? */ attempt_back_merge(q, req, max_sectors, max_segments); - /* or to the beginning? */ - } else if (req->sector - count == sector) { - if (starving) - break; - /* - * The merge_fn is a more advanced way - * of accomplishing the same task. Instead - * of applying a fixed limit of some sort - * we instead define a function which can - * determine whether or not it is safe to - * merge the request or not. - * - * See if this queue has rules that - * may suggest that we shouldn't merge - * this - */ - if(!(q->front_merge_fn)(q, req, bh, max_segments)) + goto out; + + case ELEVATOR_FRONT_MERGE: + if (!q->front_merge_fn(q, req, bh, max_segments)) break; - bh->b_reqnext = req->bh; - req->bh = bh; - req->buffer = bh->b_data; - req->current_nr_sectors = count; - req->sector = req->hard_sector = sector; - req->nr_sectors = req->hard_nr_sectors += count; + bh->b_reqnext = req->bh; + req->bh = bh; + req->buffer = bh->b_data; + req->current_nr_sectors = count; + req->sector = req->hard_sector = sector; + req->nr_sectors = req->hard_nr_sectors += count; + req->e = elevator; drive_stat_acct(req->rq_dev, req->cmd, count, 0); - - elevator_merge_before(elevator, req, latency); - attempt_front_merge(q, head, req, max_sectors, max_segments); - } else - continue; - - q->elevator.sequence++; - spin_unlock_irqrestore(&io_request_lock,flags); - return; + goto out; + /* + * elevator says don't/can't merge. get new request + */ + case ELEVATOR_NO_MERGE: + break; + default: + printk("elevator returned crap (%d)\n", el_ret); + BUG(); } - -/* find an unused request. */ -get_rq: - req = get_request(max_req, bh->b_rdev); - + /* - * if no request available: if rw_ahead, forget it, - * otherwise try again blocking.. + * Grab a free request from the freelist. Read first try their + * own queue - if that is empty, we steal from the write list. + * Writes must block if the write list is empty, and read aheads + * are not crucial. */ - if (!req) { - spin_unlock_irqrestore(&io_request_lock,flags); +get_rq: + if ((req = get_request(q, rw)) == NULL) { + spin_unlock_irq(&io_request_lock); if (rw_ahead) goto end_io; - req = __get_request_wait(max_req, bh->b_rdev); - spin_lock_irqsave(&io_request_lock,flags); - /* revalidate elevator */ - head = &q->queue_head; - if (q->head_active && !q->plugged) - head = head->next; + req = __get_request_wait(q, rw); + spin_lock_irq(&io_request_lock); } + head = &q->queue_head; + if (q->head_active && !q->plugged) + head = head->next; + /* fill up the request-info, and add it to the queue */ req->cmd = rw; req->errors = 0; @@ -747,19 +727,18 @@ get_rq: req->sem = NULL; req->bh = bh; req->bhtail = bh; - req->q = q; - add_request(q, req, head, orig_latency); - - spin_unlock_irqrestore(&io_request_lock, flags); + req->rq_dev = bh->b_rdev; + req->e = elevator; + add_request(q, req, head, latency); +out: + spin_unlock_irq(&io_request_lock); return; - end_io: bh->b_end_io(bh, test_bit(BH_Uptodate, &bh->b_state)); } int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) { - unsigned long flags; int ret; /* @@ -767,7 +746,6 @@ int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) * still free to implement/resolve their own stacking * by explicitly returning 0) */ - while (q->make_request_fn) { ret = q->make_request_fn(q, rw, bh); if (ret > 0) { @@ -781,10 +759,10 @@ int generic_make_request (request_queue_t *q, int rw, struct buffer_head * bh) * the IO request? (normal case) */ __make_request(q, rw, bh); - spin_lock_irqsave(&io_request_lock,flags); + spin_lock_irq(&io_request_lock); if (q && !q->plugged) (q->request_fn)(q); - spin_unlock_irqrestore(&io_request_lock,flags); + spin_unlock_irq(&io_request_lock); return 0; } @@ -923,28 +901,27 @@ int end_that_request_first (struct request *req, int uptodate, char *name) void end_that_request_last(struct request *req) { - if (req->q) + if (req->e) { + printk("end_that_request_last called with non-dequeued req\n"); BUG(); + } if (req->sem != NULL) up(req->sem); - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + + blkdev_release_request(req); } int __init blk_dev_init(void) { - struct request * req; struct blk_dev_struct *dev; - for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) { + request_cachep = kmem_cache_create("blkdev_requests", + sizeof(struct request), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) dev->queue = NULL; - blk_init_queue(&dev->request_queue, NULL); - } - req = all_requests + NR_REQUEST; - while (--req >= all_requests) { - req->rq_status = RQ_INACTIVE; - } memset(ro_bits,0,sizeof(ro_bits)); memset(max_readahead, 0, sizeof(max_readahead)); memset(max_sectors, 0, sizeof(max_sectors)); @@ -1070,3 +1047,4 @@ EXPORT_SYMBOL(blk_queue_headactive); EXPORT_SYMBOL(blk_queue_pluggable); EXPORT_SYMBOL(blk_queue_make_request); EXPORT_SYMBOL(generic_make_request); +EXPORT_SYMBOL(blkdev_release_request); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 96dacdc22..89018e54a 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -785,7 +785,7 @@ int __init loop_init(void) devfs_handle = devfs_mk_dir (NULL, "loop", 0, NULL); devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, &lo_fops, NULL); if ((max_loop < 1) || (max_loop > 255)) { diff --git a/drivers/block/md.c b/drivers/block/md.c index 918dbdaf2..617990e77 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -3629,7 +3629,7 @@ int md__init md_init (void) } devfs_handle = devfs_mk_dir (NULL, "md", 0, NULL); devfs_register_series (devfs_handle, "%u",MAX_MD_DEVS,DEVFS_FL_DEFAULT, - MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + MAJOR_NR, 0, S_IFBLK | S_IRUSR | S_IWUSR, &md_fops, NULL); blk_dev[MD_MAJOR].queue = md_get_queue; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 8256c57bd..222622c35 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -519,9 +519,9 @@ int nbd_init(void) } devfs_handle = devfs_mk_dir (NULL, "nbd", 0, NULL); devfs_register_series (devfs_handle, "%u", MAX_NBD, - DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, - &nbd_fops, NULL); + DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUSR | S_IWUSR, + &nbd_fops, NULL); return 0; } diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c index 3cb11286d..5c4dbbee8 100644 --- a/drivers/block/paride/pg.c +++ b/drivers/block/paride/pg.c @@ -309,7 +309,7 @@ int pg_init (void) /* preliminary initialisation */ } devfs_handle = devfs_mk_dir (NULL, "pg", 2, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pg_fops, NULL); return 0; } diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c index 739755a93..52c67d017 100644 --- a/drivers/block/paride/pt.c +++ b/drivers/block/paride/pt.c @@ -314,10 +314,10 @@ int pt_init (void) /* preliminary initialisation */ devfs_handle = devfs_mk_dir (NULL, "pt", 2, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, - major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &pt_fops, NULL); devfs_register_series (devfs_handle, "%un", 4, DEVFS_FL_DEFAULT, - major, 128, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + major, 128, S_IFCHR | S_IRUSR | S_IWUSR, &pt_fops, NULL); return 0; } diff --git a/drivers/block/rd.c b/drivers/block/rd.c index 211550d1e..26c0509d2 100644 --- a/drivers/block/rd.c +++ b/drivers/block/rd.c @@ -107,9 +107,7 @@ static struct inode *rd_inode[NUM_RAMDISKS]; /* Protected device inodes */ * architecture-specific setup routine (from the stored boot sector * information). */ - -int rd_size = CONFIG_BLK_DEV_RAM_SIZE; /* Size of the RAM disks */ - +int rd_size = 4096; /* Size of the RAM disks */ /* * It would be very desiderable to have a soft-blocksize (that in the case * of the ramdisk driver is also the hardblocksize ;) of PAGE_SIZE because @@ -413,7 +411,7 @@ int __init rd_init (void) devfs_handle = devfs_mk_dir (NULL, "rd", 0, NULL); devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUSR | S_IWUSR, 0, 0, + S_IFBLK | S_IRUSR | S_IWUSR, &fd_fops, NULL); for (i = 0; i < NUM_RAMDISKS; i++) diff --git a/drivers/block/smart1,2.h b/drivers/block/smart1,2.h index 221e4a5f3..015980013 100644 --- a/drivers/block/smart1,2.h +++ b/drivers/block/smart1,2.h @@ -62,13 +62,14 @@ static void smart4_intr_mask(ctlr_info_t *h, unsigned long val) } /* - * For this card fifo is full if reading this port returns 0! + * For older cards FIFO Full = 0. + * On this card 0 means there is room, anything else FIFO Full. * */ static unsigned long smart4_fifo_full(ctlr_info_t *h) { - return (~readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); + return (!readl(h->vaddr + S42XX_REQUEST_PORT_OFFSET)); } /* This type of controller returns -1 if the fifo is empty, @@ -81,7 +82,7 @@ static unsigned long smart4_completed(ctlr_info_t *h) = readl(h->vaddr + S42XX_REPLY_PORT_OFFSET); /* Fifo is empty */ - if( register_value == -1) + if( register_value == 0xffffffff) return 0; /* Need to let it know we got the reply */ diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index aaad8e0c6..e32238d32 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -1791,8 +1791,8 @@ int __init aztcd_init(void) return -EIO; } } - devfs_register (NULL, "aztcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &azt_fops, NULL); + devfs_register (NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL); if (devfs_register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) { printk("aztcd: Unable to get major %d for Aztech CD-ROM\n", diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 6b117a6be..caa3900b3 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -408,9 +408,9 @@ int register_cdrom(struct cdrom_device_info *cdi) } else { cdi->de = - devfs_register (devfs_handle, vname, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, vname, DEVFS_FL_DEFAULT, MAJOR (cdi->dev), MINOR (cdi->dev), - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); } cdinfo(CD_REG_UNREG, "drive \"/dev/%s\" registered\n", cdi->name); diff --git a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c index f563a16c0..b0fa47b3e 100644 --- a/drivers/cdrom/gscd.c +++ b/drivers/cdrom/gscd.c @@ -1075,8 +1075,8 @@ int result; MAJOR_NR); return -EIO; } - devfs_register (NULL, "gscd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &gscd_fops, NULL); + devfs_register (NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL); blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); blksize_size[MAJOR_NR] = gscd_blocksizes; diff --git a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c index 07f1f030c..480a01833 100644 --- a/drivers/cdrom/optcd.c +++ b/drivers/cdrom/optcd.c @@ -2064,8 +2064,8 @@ int __init optcd_init(void) printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR); return -EIO; } - devfs_register (NULL, "optcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &opt_fops, NULL); + devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &opt_fops, NULL); hardsect_size[MAJOR_NR] = &hsecsize; blksize_size[MAJOR_NR] = &blksize; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); diff --git a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c index 01dd94bf5..08400a52c 100644 --- a/drivers/cdrom/sbpcd.c +++ b/drivers/cdrom/sbpcd.c @@ -5803,9 +5803,9 @@ int __init SBPCD_INIT(void) sprintf (nbuff, "c%dt%d/cd", SBPCD_ISSUE - 1, D_S[j].drv_id); sbpcd_infop->de = - devfs_register (devfs_handle, nbuff, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, nbuff, DEVFS_FL_DEFAULT, MAJOR_NR, j, S_IFBLK | S_IRUGO | S_IWUGO, - 0, 0, &cdrom_fops, NULL); + &cdrom_fops, NULL); if (register_cdrom(sbpcd_infop)) { printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n"); diff --git a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c index 82dd5e5b9..a867c3819 100644 --- a/drivers/cdrom/sjcd.c +++ b/drivers/cdrom/sjcd.c @@ -1565,8 +1565,8 @@ int __init sjcd_init( void ){ } printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base); - devfs_register (NULL, "sjcd", 0, DEVFS_FL_DEFAULT, MAJOR_NR, 0, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, &sjcd_fops, NULL); + devfs_register (NULL, "sjcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &sjcd_fops, NULL); sjcd_present++; return( 0 ); diff --git a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c index 1f5fea7b9..d85fbc01f 100644 --- a/drivers/cdrom/sonycd535.c +++ b/drivers/cdrom/sonycd535.c @@ -1587,11 +1587,11 @@ sony535_init(void) printk("IRQ%d, ", tmp_irq); printk("using %d byte buffer\n", sony_buffer_size); - devfs_register (NULL, CDU535_HANDLE, 0, + devfs_register (NULL, CDU535_HANDLE, DEVFS_FL_DEFAULT, MAJOR_NR, 0, S_IFBLK | S_IRUGO | S_IWUGO, - 0, 0, &cdu_fops, NULL); + &cdu_fops, NULL); if (devfs_register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) { printk("Unable to get major %d for %s\n", MAJOR_NR, CDU535_MESSAGE_NAME); diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 3fc7005ed..d5091ff4d 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -226,7 +226,6 @@ int __init applicom_init(void) continue; } - /* &ac_open as dev_id? David, could you pass me this joint? */ if (request_irq(dev->irq, &ac_interrupt, SA_SHIRQ, "Applicom PCI", &ac_open)) { printk(KERN_INFO "Could not allocate IRQ %d for PCI Applicom device.\n", dev->irq); iounmap(RamIO); diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c index dc076dd5a..6fe41e118 100644 --- a/drivers/char/dsp56k.c +++ b/drivers/char/dsp56k.c @@ -519,9 +519,9 @@ int __init dsp56k_init(void) printk("DSP56k driver: Unable to register driver\n"); return -ENODEV; } - devfs_handle = devfs_register (NULL, "dsp56k", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register (NULL, "dsp56k", DEVFS_FL_DEFAULT, DSP56K_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &dsp56k_fops, NULL); dsp56k.in_use = 0; diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index 730b69b17..475e1d052 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -352,9 +352,9 @@ static int __init dtlk_init(void) } if (dtlk_dev_probe() == 0) printk(", MAJOR %d\n", dtlk_major); - devfs_handle = devfs_register (NULL, "dtlk", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register (NULL, "dtlk", DEVFS_FL_DEFAULT, dtlk_major, DTLK_MINOR, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &dtlk_fops, NULL); init_timer(&dtlk_timer); diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c index 883f4a106..e6cb87d1e 100644 --- a/drivers/char/ftape/zftape/zftape-init.c +++ b/drivers/char/ftape/zftape/zftape-init.c @@ -439,34 +439,34 @@ KERN_INFO char devname[9]; sprintf (devname, "qft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 4, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "zqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 16, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nzqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 20, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "rawqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 32, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); sprintf (devname, "nrawqft%i", i); - devfs_register (NULL, devname, 0, DEVFS_FL_NONE, + devfs_register (NULL, devname, DEVFS_FL_DEFAULT, QIC117_TAPE_MAJOR, i + 36, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &zft_cdev, NULL); } diff --git a/drivers/char/i2c-old.c b/drivers/char/i2c-old.c index b509c9a18..bd9750fc3 100644 --- a/drivers/char/i2c-old.c +++ b/drivers/char/i2c-old.c @@ -37,8 +37,8 @@ static struct i2c_driver *drivers[I2C_DRIVER_MAX]; static int bus_count = 0, driver_count = 0; #ifdef CONFIG_VIDEO_BT848 -extern int tuner_init_module(void); -extern int msp3400_init_module(void); +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); #endif #ifdef CONFIG_VIDEO_BUZ extern int saa7111_init(void); @@ -55,8 +55,8 @@ int i2c_init(void) scan ? " (i2c bus scan enabled)" : ""); /* anything to do here ? */ #ifdef CONFIG_VIDEO_BT848 - tuner_init_module(); - msp3400_init_module(); + i2c_tuner_init(); + msp3400c_init(); #endif #ifdef CONFIG_VIDEO_BUZ saa7111_init(); diff --git a/drivers/char/ip2main.c b/drivers/char/ip2main.c index 033eb9aaf..a645832c4 100644 --- a/drivers/char/ip2main.c +++ b/drivers/char/ip2main.c @@ -877,19 +877,19 @@ old_ip2_init(void) #ifdef CONFIG_DEVFS_FS sprintf( name, "ipl%d", i ); i2BoardPtrTable[i]->devfs_ipl_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); sprintf( name, "stat%d", i ); i2BoardPtrTable[i]->devfs_stat_handle = - devfs_register (devfs_handle, name, 0, - DEVFS_FL_NONE, + devfs_register (devfs_handle, name, + DEVFS_FL_DEFAULT, IP2_IPL_MAJOR, 4 * i + 1, S_IRUSR | S_IWUSR | S_IRGRP | S_IFCHR, - 0, 0, &ip2_ipl, NULL); + &ip2_ipl, NULL); for ( box = 0; box < ABS_MAX_BOXES; ++box ) { diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 8d16c9daf..81421c032 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -5310,7 +5310,7 @@ int __init stli_init(void) devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &stli_fsiomem, NULL); /* diff --git a/drivers/char/joystick/Config.in b/drivers/char/joystick/Config.in index 3e371744e..1547e5f38 100644 --- a/drivers/char/joystick/Config.in +++ b/drivers/char/joystick/Config.in @@ -1,34 +1,56 @@ # -# Joystick driver +# Joystick driver configuration # mainmenu_option next_comment comment 'Joysticks' tristate 'Joystick support' CONFIG_JOYSTICK - if [ "$CONFIG_JOYSTICK" != "n" ]; then - dep_tristate ' Classic PC analog' CONFIG_JOY_ANALOG $CONFIG_JOYSTICK - dep_tristate ' FPGaming and MadCatz A3D' CONFIG_JOY_ASSASSIN $CONFIG_JOYSTICK - dep_tristate ' Gravis GrIP' CONFIG_JOY_GRAVIS $CONFIG_JOYSTICK - dep_tristate ' Logitech ADI' CONFIG_JOY_LOGITECH $CONFIG_JOYSTICK - dep_tristate ' Microsoft SideWinder' CONFIG_JOY_SIDEWINDER $CONFIG_JOYSTICK - dep_tristate ' ThrustMaster DirectConnect' CONFIG_JOY_THRUSTMASTER $CONFIG_JOYSTICK - dep_tristate ' Creative Labs Blaster' CONFIG_JOY_CREATIVE $CONFIG_JOYSTICK - dep_tristate ' PDPI Lightning 4 card' CONFIG_JOY_LIGHTNING $CONFIG_JOYSTICK - dep_tristate ' Trident 4DWave and Aureal Vortex gameport' CONFIG_JOY_PCI $CONFIG_JOYSTICK - dep_tristate ' Magellan and Space Mouse' CONFIG_JOY_MAGELLAN $CONFIG_JOYSTICK - dep_tristate ' SpaceTec SpaceOrb 360 and SpaceBall Avenger' CONFIG_JOY_SPACEORB $CONFIG_JOYSTICK - dep_tristate ' SpaceTec SpaceBall 4000 FLX' CONFIG_JOY_SPACEBALL $CONFIG_JOYSTICK - dep_tristate ' Logitech WingMan Warrior' CONFIG_JOY_WARRIOR $CONFIG_JOYSTICK + + define_tristate CONFIG_USB $CONFIG_JOYSTICK + define_tristate CONFIG_INPUT_JOYDEV $CONFIG_JOYSTICK + + comment 'Game port support' + dep_tristate ' ns558 gameports' CONFIG_INPUT_NS558 $CONFIG_JOYSTICK + dep_tristate ' PDPI Lightning 4 gamecard' CONFIG_INPUT_LIGHTNING $CONFIG_JOYSTICK + dep_tristate ' Aureal Vortex and Trident 4DWave gameports' CONFIG_INPUT_PCIGAME $CONFIG_JOYSTICK + + comment 'Gameport joysticks' + dep_tristate ' Classic PC analog joysticks and gamepads' CONFIG_INPUT_ANALOG $CONFIG_JOYSTICK + dep_tristate ' Assasin 3D and MadCatz Panther devices' CONFIG_INPUT_A3D $CONFIG_JOYSTICK + dep_tristate ' Logitech ADI digital joysticks and gamepads' CONFIG_INPUT_ADI $CONFIG_JOYSTICK + dep_tristate ' Creative Labs Blaster Cobra gamepad' CONFIG_INPUT_COBRA $CONFIG_JOYSTICK + dep_tristate ' Genius Flight2000 Digital joysticks and gamepads' CONFIG_INPUT_GF2K $CONFIG_JOYSTICK + dep_tristate ' Gravis GrIP joysticks and gamepads' CONFIG_INPUT_GRIP $CONFIG_JOYSTICK + dep_tristate ' InterAct digital joysticks and gamepads' CONFIG_INPUT_INTERACT $CONFIG_JOYSTICK + dep_tristate ' ThrustMaster DirectConnect joysticks and gamepads' CONFIG_INPUT_TMDC $CONFIG_JOYSTICK + dep_tristate ' Microsoft SideWinder digital joysticks and gamepads' CONFIG_INPUT_SIDEWINDER $CONFIG_JOYSTICK + + comment 'Serial port support' + dep_tristate ' Serial port input line discipline' CONFIG_INPUT_SERPORT $CONFIG_JOYSTICK + + comment 'Serial port joysticks' + dep_tristate ' Logitech WingMan Warrior joystick' CONFIG_INPUT_WARRIOR $CONFIG_JOYSTICK + dep_tristate ' LogiCad3d Magellan/SpaceMouse 6dof controller' CONFIG_INPUT_MAGELLAN $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceOrb/Avenger 6dof controller' CONFIG_INPUT_SPACEORB $CONFIG_JOYSTICK + dep_tristate ' SpaceTec SpaceBall 4000 FLX 6dof controller' CONFIG_INPUT_SPACEBALL $CONFIG_JOYSTICK + dep_tristate ' I-Force joysticks/wheels' CONFIG_INPUT_IFORCE_232 $CONFIG_JOYSTICK + if [ "$CONFIG_INPUT_IFORCE_232" != "n" ]; then + define_tristate CONFIG_INPUT_IFORCE $CONFIG_INPUT_IFORCE_232 + fi + if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate ' NES, SNES, PSX, N64, Multi' CONFIG_JOY_CONSOLE $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' Sega, Multi' CONFIG_JOY_DB9 $CONFIG_JOYSTICK $CONFIG_PARPORT - dep_tristate ' TurboGraFX interface' CONFIG_JOY_TURBOGRAFX $CONFIG_JOYSTICK $CONFIG_PARPORT - fi + comment 'Parallel port joysticks' + dep_tristate ' Multisystem, Sega Genesis, Saturn joysticks and gamepads' CONFIG_INPUT_DB9 $CONFIG_JOYSTICK + dep_tristate ' Multisystem, NES, SNES, N64, PSX joysticks and gamepads' CONFIG_INPUT_GAMECON $CONFIG_JOYSTICK + dep_tristate ' Multisystem joysticks via TurboGraFX device' CONFIG_INPUT_TURBOGRAFX $CONFIG_JOYSTICK + fi + if [ "$CONFIG_AMIGA" = "y" ]; then - dep_tristate ' Amiga joysticks' CONFIG_JOY_AMIGA $CONFIG_JOYSTICK + comment 'System joysticks' + dep_tristate ' Amiga joysticks' CONFIG_INPUT_AMIJOY $CONFIG_JOYSTICK fi fi - + endmenu diff --git a/drivers/char/joystick/Makefile b/drivers/char/joystick/Makefile index 2bb5870b0..2ad452618 100644 --- a/drivers/char/joystick/Makefile +++ b/drivers/char/joystick/Makefile @@ -2,154 +2,73 @@ # Makefile for the joystick drivers. # -O_TARGET := js.o -OX_OBJS := -O_OBJS := -MX_OBJS := -M_OBJS := - -ifeq ($(CONFIG_JOYSTICK),y) -OX_OBJS += joystick.o -else - ifeq ($(CONFIG_JOYSTICK),m) - MX_OBJS += joystick.o - endif -endif - -ifeq ($(CONFIG_JOY_AMIGA),y) -O_OBJS += joy-amiga.o -else - ifeq ($(CONFIG_JOY_AMIGA),m) - M_OBJS += joy-amiga.o - endif -endif - -ifeq ($(CONFIG_JOY_ANALOG),y) -O_OBJS += joy-analog.o -else - ifeq ($(CONFIG_JOY_ANALOG),m) - M_OBJS += joy-analog.o - endif -endif - -ifeq ($(CONFIG_JOY_ASSASSIN),y) -O_OBJS += joy-assassin.o -else - ifeq ($(CONFIG_JOY_ASSASSIN),m) - M_OBJS += joy-assassin.o - endif -endif - -ifeq ($(CONFIG_JOY_CONSOLE),y) -O_OBJS += joy-console.o -else - ifeq ($(CONFIG_JOY_CONSOLE),m) - M_OBJS += joy-console.o - endif -endif - -ifeq ($(CONFIG_JOY_CREATIVE),y) -O_OBJS += joy-creative.o -else - ifeq ($(CONFIG_JOY_CREATIVE),m) - M_OBJS += joy-creative.o - endif -endif - -ifeq ($(CONFIG_JOY_DB9),y) -O_OBJS += joy-db9.o -else - ifeq ($(CONFIG_JOY_DB9),m) - M_OBJS += joy-db9.o - endif -endif - -ifeq ($(CONFIG_JOY_GRAVIS),y) -O_OBJS += joy-gravis.o -else - ifeq ($(CONFIG_JOY_GRAVIS),m) - M_OBJS += joy-gravis.o - endif -endif - -ifeq ($(CONFIG_JOY_LIGHTNING),y) -O_OBJS += joy-lightning.o -else - ifeq ($(CONFIG_JOY_LIGHTNING),m) - M_OBJS += joy-lightning.o - endif -endif - -ifeq ($(CONFIG_JOY_LOGITECH),y) -O_OBJS += joy-logitech.o -else - ifeq ($(CONFIG_JOY_LOGITECH),m) - M_OBJS += joy-logitech.o - endif -endif - -ifeq ($(CONFIG_JOY_MAGELLAN),y) -O_OBJS += joy-magellan.o -else - ifeq ($(CONFIG_JOY_MAGELLAN),m) - M_OBJS += joy-magellan.o - endif -endif - -ifeq ($(CONFIG_JOY_PCI),y) -O_OBJS += joy-pci.o -else - ifeq ($(CONFIG_JOY_PCI),m) - M_OBJS += joy-pci.o - endif -endif - -ifeq ($(CONFIG_JOY_SIDEWINDER),y) -O_OBJS += joy-sidewinder.o -else - ifeq ($(CONFIG_JOY_SIDEWINDER),m) - M_OBJS += joy-sidewinder.o - endif -endif - -ifeq ($(CONFIG_JOY_SPACEORB),y) -O_OBJS += joy-spaceorb.o -else - ifeq ($(CONFIG_JOY_SPACEORB),m) - M_OBJS += joy-spaceorb.o - endif -endif - -ifeq ($(CONFIG_JOY_SPACEBALL),y) -O_OBJS += joy-spaceball.o -else - ifeq ($(CONFIG_JOY_SPACEBALL),m) - M_OBJS += joy-spaceball.o - endif -endif - -ifeq ($(CONFIG_JOY_THRUSTMASTER),y) -O_OBJS += joy-thrustmaster.o -else - ifeq ($(CONFIG_JOY_THRUSTMASTER),m) - M_OBJS += joy-thrustmaster.o - endif -endif - -ifeq ($(CONFIG_JOY_TURBOGRAFX),y) -O_OBJS += joy-turbografx.o -else - ifeq ($(CONFIG_JOY_TURBOGRAFX),m) - M_OBJS += joy-turbografx.o - endif -endif - -ifeq ($(CONFIG_JOY_WARRIOR),y) -O_OBJS += joy-warrior.o -else - ifeq ($(CONFIG_JOY_WARRIOR),m) - M_OBJS += joy-warrior.o - endif -endif +# Subdirs. + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) + +# The target object and module list name. + +O_TARGET := js.o +M_OBJS := +O_OBJS := +#MOD_LIST_NAME := INPUT_MODULES + +# Objects that export symbols. + +export-objs := serio.o gameport.o + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_INPUT_SERPORT) += serport.o serio.o + +obj-$(CONFIG_INPUT_NS558) += ns558.o gameport.o +obj-$(CONFIG_INPUT_LIGHTNING) += lightning.o gameport.o +obj-$(CONFIG_INPUT_PCIGAME) += pcigame.o gameport.o + +obj-$(CONFIG_INPUT_WARRIOR) += warrior.o serio.o +obj-$(CONFIG_INPUT_MAGELLAN) += magellan.o serio.o +obj-$(CONFIG_INPUT_SPACEORB) += spaceorb.o serio.o +obj-$(CONFIG_INPUT_SPACEBALL) += spaceball.o serio.o +obj-$(CONFIG_INPUT_IFORCE_232) += serio.o + +obj-$(CONFIG_INPUT_ANALOG) += analog.o gameport.o +obj-$(CONFIG_INPUT_A3D) += a3d.o gameport.o +obj-$(CONFIG_INPUT_ADI) += adi.o gameport.o +obj-$(CONFIG_INPUT_COBRA) += cobra.o gameport.o +obj-$(CONFIG_INPUT_GF2K) += gf2k.o gameport.o +obj-$(CONFIG_INPUT_GRIP) += grip.o gameport.o +obj-$(CONFIG_INPUT_INTERACT) += interact.o gameport.o +obj-$(CONFIG_INPUT_TMDC) += tmdc.o gameport.o +obj-$(CONFIG_INPUT_SIDEWINDER) += sidewinder.o gameport.o + +obj-$(CONFIG_INPUT_DB9) += db9.o +obj-$(CONFIG_INPUT_GAMECON) += gamecon.o +obj-$(CONFIG_INPUT_TURBOGRAFX) += turbografx.o + +obj-$(CONFIG_INPUT_AMIJOY) += amijoy.o + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + +# The global Rules.make. include $(TOPDIR)/Rules.make diff --git a/drivers/char/joystick/a3d.c b/drivers/char/joystick/a3d.c new file mode 100644 index 000000000..bd17f8de2 --- /dev/null +++ b/drivers/char/joystick/a3d.c @@ -0,0 +1,387 @@ +/* + * $Id: a3d.c,v 1.10 2000/05/29 11:19:50 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * FP-Gaming Assasin 3D joystick driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define A3D_MAX_START 400 /* 400 us */ +#define A3D_MAX_STROBE 60 /* 40 us */ +#define A3D_DELAY_READ 3 /* 3 ms */ +#define A3D_MAX_LENGTH 40 /* 40*3 bits */ +#define A3D_REFRESH_TIME HZ/50 /* 20 ms */ + +#define A3D_MODE_A3D 1 /* Assassin 3D */ +#define A3D_MODE_PAN 2 /* Panther */ +#define A3D_MODE_OEM 3 /* Panther OEM version */ +#define A3D_MODE_PXL 4 /* Panther XL */ + +char *a3d_names[] = { NULL, "FP-Gaming Assassin 3D", "MadCatz Panther", "OEM Panther", + "MadCatz Panther XL", "MadCatz Panther XL w/ rudder" }; + +struct a3d { + struct gameport *gameport; + struct gameport adc; + struct input_dev dev; + struct timer_list timer; + int axes[4]; + int buttons; + int mode; + int length; + int used; + int reads; + int bads; +}; + +/* + * a3d_read_packet() reads an Assassin 3D packet. + */ + +static int a3d_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + t = gameport_time(gameport, A3D_MAX_START); + s = gameport_time(gameport, A3D_MAX_STROBE); + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (~v & u & 0x10) { + data[i++] = v >> 5; + t = s; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * a3d_csum() computes checksum of triplet packet + */ + +static int a3d_csum(char *data, int count) +{ + int i, csum = 0; + for (i = 0; i < count - 2; i++) csum += data[i]; + return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); +} + +static void a3d_read(struct a3d *a3d, unsigned char *data) +{ + struct input_dev *dev = &a3d->dev; + + switch (a3d->mode) { + + case A3D_MODE_A3D: + case A3D_MODE_OEM: + case A3D_MODE_PAN: + + input_report_rel(dev, REL_X, ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + + a3d->axes[0] = ((signed char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; + a3d->axes[1] = ((signed char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; + a3d->axes[2] = ((signed char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; + a3d->axes[3] = ((signed char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; + + a3d->buttons = ((data[3] << 3) | data[4]) & 0xf; + + return; + + case A3D_MODE_PXL: + + input_report_rel(dev, REL_X, ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7)); + input_report_rel(dev, REL_Y, ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7)); + + input_report_key(dev, BTN_RIGHT, data[2] & 1); + input_report_key(dev, BTN_LEFT, data[3] & 2); + input_report_key(dev, BTN_MIDDLE, data[3] & 4); + input_report_key(dev, BTN_SIDE, data[7] & 2); + input_report_key(dev, BTN_EXTRA, data[7] & 4); + + input_report_abs(dev, ABS_X, ((signed char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128); + input_report_abs(dev, ABS_Y, ((signed char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128); + input_report_abs(dev, ABS_RUDDER, ((signed char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128); + input_report_abs(dev, ABS_THROTTLE, ((signed char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128); + + input_report_abs(dev, ABS_HAT0X, ( data[5] & 1) - ((data[5] >> 2) & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[4] >> 1) & 1) - ( data[3] & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[4] >> 2) & 1) - ( data[4] & 1)); + + input_report_key(dev, BTN_TRIGGER, data[8] & 1); + input_report_key(dev, BTN_THUMB, data[8] & 2); + input_report_key(dev, BTN_TOP, data[8] & 4); + input_report_key(dev, BTN_PINKIE, data[7] & 1); + + return; + } +} + + +/* + * a3d_timer() reads and analyzes A3D joystick data. + */ + +static void a3d_timer(unsigned long private) +{ + struct a3d *a3d = (void *) private; + unsigned char data[A3D_MAX_LENGTH]; + a3d->reads++; + if (a3d_read_packet(a3d->gameport, a3d->length, data) != a3d->length + || data[0] != a3d->mode || a3d_csum(data, a3d->length)) + a3d->bads++; else a3d_read(a3d, data); + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); +} + +/* + * a3d_adc_cooked_read() copies the acis and button data to the + * callers arrays. It could do the read itself, but the caller could + * call this more than 50 times a second, which would use too much CPU. + */ + +int a3d_adc_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct a3d *a3d = gameport->driver; + int i; + for (i = 0; i < 4; i++) + axes[i] = (a3d->axes[i] < 254) ? a3d->axes[i] : -1; + *buttons = a3d->buttons; + return 0; +} + +/* + * a3d_adc_open() is the gameport open routine. It refuses to serve + * any but cooked data. + */ + +int a3d_adc_open(struct gameport *gameport, int mode) +{ + struct a3d *a3d = gameport->driver; + if (mode != GAMEPORT_MODE_COOKED) + return -1; + if (!a3d->used++) + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); + return 0; +} + +/* + * a3d_adc_close() is a callback from the input close routine. + */ + +static void a3d_adc_close(struct gameport *gameport) +{ + struct a3d *a3d = gameport->driver; + if (!--a3d->used) + del_timer(&a3d->timer); +} + +/* + * a3d_open() is a callback from the input open routine. + */ + +static int a3d_open(struct input_dev *dev) +{ + struct a3d *a3d = dev->private; + if (!a3d->used++) + mod_timer(&a3d->timer, jiffies + A3D_REFRESH_TIME); + return 0; +} + +/* + * a3d_close() is a callback from the input close routine. + */ + +static void a3d_close(struct input_dev *dev) +{ + struct a3d *a3d = dev->private; + if (!--a3d->used) + del_timer(&a3d->timer); +} + +/* + * a3d_connect() probes for A3D joysticks. + */ + +static void a3d_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct a3d *a3d; + unsigned char data[A3D_MAX_LENGTH]; + int i; + + if (!(a3d = kmalloc(sizeof(struct a3d), GFP_KERNEL))) + return; + memset(a3d, 0, sizeof(struct a3d)); + + gameport->private = a3d; + + a3d->gameport = gameport; + init_timer(&a3d->timer); + a3d->timer.data = (long) a3d; + a3d->timer.function = a3d_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = a3d_read_packet(gameport, A3D_MAX_LENGTH, data); + + if (!i || a3d_csum(data, i)) + goto fail2; + + a3d->mode = data[0]; + + if (!a3d->mode || a3d->mode > 5) { + printk(KERN_WARNING "a3d.c: Unknown A3D device detected " + "(gameport%d, id=%d), contact <vojtech@suse.cz>\n", gameport->number, a3d->mode); + goto fail2; + } + + + if (a3d->mode == A3D_MODE_PXL) { + + int axes[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER }; + + a3d->length = 33; + + a3d->dev.evbit[0] |= BIT(EV_ABS) | BIT(EV_KEY) | BIT(EV_REL); + a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); + a3d->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_RUDDER) + | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) | BIT(ABS_HAT1X) | BIT(ABS_HAT1Y); + + a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE) + | BIT(BTN_SIDE) | BIT(BTN_EXTRA); + + a3d->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_PINKIE); + + a3d_read(a3d, data); + + for (i = 0; i < 4; i++) { + if (i < 2) { + a3d->dev.absmin[axes[i]] = 48; + a3d->dev.absmax[axes[i]] = a3d->dev.abs[axes[i]] * 2 - 48; + a3d->dev.absflat[axes[i]] = 8; + } else { + a3d->dev.absmin[axes[i]] = 2; + a3d->dev.absmax[axes[i]] = 253; + } + a3d->dev.absmin[ABS_HAT0X + i] = -1; + a3d->dev.absmax[ABS_HAT0X + i] = 1; + } + + } else { + a3d->length = 29; + + a3d->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_REL); + a3d->dev.relbit[0] |= BIT(REL_X) | BIT(REL_Y); + a3d->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_RIGHT) | BIT(BTN_LEFT) | BIT(BTN_MIDDLE); + + a3d->adc.driver = a3d; + a3d->adc.open = a3d_adc_open; + a3d->adc.close = a3d_adc_close; + a3d->adc.cooked_read = a3d_adc_cooked_read; + a3d->adc.fuzz = 1; + a3d->adc.type = GAMEPORT_EXT; + + a3d_read(a3d, data); + + gameport_register_port(&a3d->adc); + printk(KERN_INFO "gameport%d: %s on gameport%d.0\n", + a3d->adc.number, a3d_names[a3d->mode], gameport->number); + } + + a3d->dev.private = a3d; + a3d->dev.open = a3d_open; + a3d->dev.close = a3d_close; + + a3d->dev.name = a3d_names[a3d->mode]; + a3d->dev.idbus = BUS_GAMEPORT; + a3d->dev.idvendor = GAMEPORT_ID_VENDOR_MADCATZ; + a3d->dev.idproduct = a3d->mode; + a3d->dev.idversion = 0x0100; + + input_register_device(&a3d->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + a3d->dev.number, a3d_names[a3d->mode], gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(a3d); +} + +static void a3d_disconnect(struct gameport *gameport) +{ + + struct a3d *a3d = gameport->private; + input_unregister_device(&a3d->dev); + if (a3d->mode < A3D_MODE_PXL) + gameport_unregister_port(&a3d->adc); + gameport_close(gameport); + kfree(a3d); +} + +static struct gameport_dev a3d_dev = { + connect: a3d_connect, + disconnect: a3d_disconnect, +}; + +int __init a3d_init(void) +{ + gameport_register_device(&a3d_dev); + return 0; +} + +void __exit a3d_exit(void) +{ + gameport_unregister_device(&a3d_dev); +} + +module_init(a3d_init); +module_exit(a3d_exit); diff --git a/drivers/char/joystick/adi.c b/drivers/char/joystick/adi.c new file mode 100644 index 000000000..3195fce03 --- /dev/null +++ b/drivers/char/joystick/adi.c @@ -0,0 +1,554 @@ +/* + * $Id: adi.c,v 1.12 2000/06/03 20:18:52 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Logitech ADI joystick family driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/gameport.h> +#include <linux/init.h> + +/* + * Times, array sizes, flags, ids. + */ + +#define ADI_MAX_START 200 /* Trigger to packet timeout [200us] */ +#define ADI_MAX_STROBE 40 /* Single bit timeout [40us] */ +#define ADI_REFRESH_TIME HZ/50 /* How often to poll the joystick [20 ms] */ +#define ADI_INIT_DELAY 10 /* Delay after init packet [10ms] */ +#define ADI_DATA_DELAY 4 /* Delay after data packet [4ms] */ + +#define ADI_MAX_LENGTH 256 +#define ADI_MIN_LENGTH 8 +#define ADI_MIN_LEN_LENGTH 10 +#define ADI_MIN_ID_LENGTH 66 +#define ADI_MAX_NAME_LENGTH 48 +#define ADI_MAX_CNAME_LENGTH 16 + +#define ADI_FLAG_HAT 0x04 +#define ADI_FLAG_10BIT 0x08 + +#define ADI_ID_TPD 0x01 +#define ADI_ID_WGP 0x06 +#define ADI_ID_WGPE 0x08 +#define ADI_ID_MAX 0x0a + +/* + * Names, buttons, axes ... + */ + +static char *adi_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", + "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", + "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", + "WingMan GamePad USB", "Unknown Device %#x" }; + +static char adi_wmgpe_abs[] = { ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y }; +static char adi_wmi_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static char adi_wmed3d_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RZ, ABS_HAT0X, ABS_HAT0Y }; +static char adi_cm2_abs[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char adi_wmf_abs[] = { ABS_WHEEL, ABS_GAS, ABS_BRAKE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; + +static short adi_wmgpe_key[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }; +static short adi_wmi_key[] = { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_EXTRA }; +static short adi_wmed3d_key[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2 }; +static short adi_cm2_key[] = { BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8 }; + +static char* adi_abs[] = { adi_wmi_abs, adi_wmgpe_abs, adi_wmf_abs, adi_cm2_abs, adi_wmi_abs, adi_wmf_abs, + adi_wmgpe_abs, adi_wmed3d_abs, adi_wmgpe_abs, adi_wmgpe_abs, adi_wmi_abs }; + +static short* adi_key[] = { adi_wmi_key, adi_wmgpe_key, adi_cm2_key, adi_cm2_key, adi_wmi_key, adi_cm2_key, + adi_wmgpe_key, adi_wmed3d_key, adi_wmgpe_key, adi_wmgpe_key, adi_wmi_key }; + +/* + * Hat to axis conversion arrays. + */ + +static struct { + int x; + int y; +} adi_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +/* + * Per-port information. + */ + +struct adi { + struct input_dev dev; + int length; + int ret; + int idx; + unsigned char id; + char buttons; + char axes10; + char axes8; + signed char pad; + char hats; + char *abs; + short *key; + char name[ADI_MAX_NAME_LENGTH]; + char cname[ADI_MAX_CNAME_LENGTH]; + unsigned char data[ADI_MAX_LENGTH]; +}; + +struct adi_port { + struct gameport *gameport; + struct timer_list timer; + struct adi adi[2]; + int bad; + int reads; + int used; +}; + +/* + * adi_read_packet() reads a Logitech ADI packet. + */ + +static void adi_read_packet(struct adi_port *port) +{ + struct adi *adi = port->adi; + struct gameport *gameport = port->gameport; + unsigned char u, v, w, x, z; + int t[2], s[2], i; + unsigned long flags; + + for (i = 0; i < 2; i++) { + adi[i].ret = -1; + t[i] = gameport_time(gameport, ADI_MAX_START); + s[i] = 0; + } + + __save_flags(flags); + __cli(); + + gameport_trigger(gameport); + v = z = gameport_read(gameport); + + do { + u = v; + w = u ^ (v = x = gameport_read(gameport)); + for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { + t[i]--; + if ((w & 0x30) && s[i]) { + if ((w & 0x30) < 0x30 && adi[i].ret < ADI_MAX_LENGTH && t[i] > 0) { + adi[i].data[++adi[i].ret] = w; + t[i] = gameport_time(gameport, ADI_MAX_STROBE); + } else t[i] = 0; + } else if (!(x & 0x30)) s[i] = 1; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + return; +} + +/* + * adi_move_bits() detects a possible 2-stream mode, and moves + * the bits accordingly. + */ + +static void adi_move_bits(struct adi_port *port, int length) +{ + int i; + struct adi *adi = port->adi; + + adi[0].idx = adi[1].idx = 0; + + if (adi[0].ret <= 0 || adi[1].ret <= 0) return; + if (adi[0].data[0] & 0x20 || ~adi[1].data[0] & 0x20) return; + + for (i = 1; i <= adi[1].ret; i++) + adi[0].data[((length - 1) >> 1) + i + 1] = adi[1].data[i]; + + adi[0].ret += adi[1].ret; + adi[1].ret = -1; +} + +/* + * adi_get_bits() gathers bits from the data packet. + */ + +static inline int adi_get_bits(struct adi *adi, int count) +{ + int bits = 0; + int i; + if ((adi->idx += count) > adi->ret) return 0; + for (i = 0; i < count; i++) + bits |= ((adi->data[adi->idx - i] >> 5) & 1) << i; + return bits; +} + +/* + * adi_decode() decodes Logitech joystick data into input events. + */ + +static int adi_decode(struct adi *adi) +{ + struct input_dev *dev = &adi->dev; + char *abs = adi->abs; + short *key = adi->key; + int i, t; + + if (adi->ret < adi->length || adi->id != (adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4))) + return -1; + + for (i = 0; i < adi->axes10; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 10)); + + for (i = 0; i < adi->axes8; i++) + input_report_abs(dev, *abs++, adi_get_bits(adi, 8)); + + for (i = 0; i < adi->buttons && i < 63; i++) { + if (i == adi->pad) { + t = adi_get_bits(adi, 4); + input_report_abs(dev, *abs++, ((t >> 2) & 1) - ( t & 1)); + input_report_abs(dev, *abs++, ((t >> 1) & 1) - ((t >> 3) & 1)); + } + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + } + + for (i = 0; i < adi->hats; i++) { + if ((t = adi_get_bits(adi, 4)) > 8) t = 0; + input_report_abs(dev, *abs++, adi_hat_to_axis[t].x); + input_report_abs(dev, *abs++, adi_hat_to_axis[t].y); + } + + for (i = 63; i < adi->buttons; i++) + input_report_key(dev, *key++, adi_get_bits(adi, 1)); + + return 0; +} + +/* + * adi_read() reads the data packet and decodes it. + */ + +static int adi_read(struct adi_port *port) +{ + int i; + int result = 0; + + adi_read_packet(port); + adi_move_bits(port, port->adi[0].length); + + for (i = 0; i < 2; i++) + if (port->adi[i].length) + result |= adi_decode(port->adi + i); + + return result; +} + +/* + * adi_timer() repeatedly polls the Logitech joysticks. + */ + +static void adi_timer(unsigned long data) +{ + struct adi_port *port = (void *) data; + port->bad -= adi_read(port); + port->reads++; + mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME); +} + +/* + * adi_open() is a callback from the input open routine. + */ + +static int adi_open(struct input_dev *dev) +{ + struct adi_port *port = dev->private; + if (!port->used++) + mod_timer(&port->timer, jiffies + ADI_REFRESH_TIME); + return 0; +} + +/* + * adi_close() is a callback from the input close routine. + */ + +static void adi_close(struct input_dev *dev) +{ + struct adi_port *port = dev->private; + if (!--port->used) + del_timer(&port->timer); +} + +/* + * adi_init_digital() sends a trigger & delay sequence + * to reset and initialize a Logitech joystick into digital mode. + */ + +static void adi_init_digital(struct gameport *gameport) +{ + int seq[] = { 3, -2, -3, 10, -6, -11, -7, -9, 11, 0 }; + int i; + + for (i = 0; seq[i]; i++) { + gameport_trigger(gameport); + if (seq[i] > 0) wait_ms(seq[i]); + if (seq[i] < 0) mdelay(-seq[i]); + } +} + +static void adi_id_decode(struct adi *adi, struct adi_port *port) +{ + int i, t; + + if (adi->ret < ADI_MIN_ID_LENGTH) /* Minimum ID packet length */ + return; + + if (adi->ret < (t = adi_get_bits(adi, 10))) { + printk(KERN_WARNING "adi: Short ID packet: reported: %d != read: %d\n", t, adi->ret); + return; + } + + adi->id = adi_get_bits(adi, 4) | (adi_get_bits(adi, 4) << 4); + + if ((t = adi_get_bits(adi, 4)) & ADI_FLAG_HAT) adi->hats++; + + adi->length = adi_get_bits(adi, 10); + + if (adi->length >= ADI_MAX_LENGTH || adi->length < ADI_MIN_LENGTH) { + printk(KERN_WARNING "adi: Bad data packet length (%d).\n", adi->length); + adi->length = 0; + return; + } + + adi->axes8 = adi_get_bits(adi, 4); + adi->buttons = adi_get_bits(adi, 6); + + if (adi_get_bits(adi, 6) != 8 && adi->hats) { + printk(KERN_WARNING "adi: Other than 8-dir POVs not supported yet.\n"); + adi->length = 0; + return; + } + + adi->buttons += adi_get_bits(adi, 6); + adi->hats += adi_get_bits(adi, 4); + + i = adi_get_bits(adi, 4); + + if (t & ADI_FLAG_10BIT) { + adi->axes10 = adi->axes8 - i; + adi->axes8 = i; + } + + t = adi_get_bits(adi, 4); + + for (i = 0; i < t; i++) + adi->cname[i] = adi_get_bits(adi, 8); + adi->cname[i] = 0; + + if (adi->length != (t = 8 + adi->buttons + adi->axes10 * 10 + adi->axes8 * 8 + adi->hats * 4)) { + printk(KERN_WARNING "adi: Expected length %d != data length %d\n", t, adi->length); + adi->length = 0; + return; + } + + switch (adi->id) { + case ADI_ID_TPD: + adi->pad = 4; + adi->buttons -= 4; + break; + case ADI_ID_WGP: + adi->pad = 0; + adi->buttons -= 4; + break; + default: + adi->pad = -1; + break; + } +} + +static void adi_init_input(struct adi *adi, struct adi_port *port) +{ + int i, t; + char buf[ADI_MAX_NAME_LENGTH]; + + if (!adi->length) return; + + t = adi->id < ADI_ID_MAX ? adi->id : ADI_ID_MAX; + + sprintf(buf, adi_names[t], adi->id); + sprintf(adi->name, "Logitech %s", buf); + + adi->abs = adi_abs[t]; + adi->key = adi_key[t]; + + adi->dev.open = adi_open; + adi->dev.close = adi_close; + + adi->dev.name = adi->name; + adi->dev.idbus = BUS_GAMEPORT; + adi->dev.idvendor = GAMEPORT_ID_VENDOR_LOGITECH; + adi->dev.idproduct = adi->id; + adi->dev.idversion = 0x0100; + + adi->dev.private = port; + adi->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < adi->axes10 + adi->axes8 + adi->hats * 2; i++) + set_bit(adi->abs[i], &adi->dev.absbit); + + for (i = 0; i < adi->buttons; i++) + set_bit(adi->key[i], &adi->dev.keybit); +} + +static void adi_init_center(struct adi *adi) +{ + int i, t, x; + + if (!adi->length) return; + + for (i = 0; i < adi->axes10 + adi->axes8 + adi->hats * 2; i++) { + + t = adi->abs[i]; + x = adi->dev.abs[t]; + + if (t == ABS_THROTTLE || t == ABS_RUDDER || adi->id == ADI_ID_WGPE) { + if (i < adi->axes10) x = 512; else x = 128; + } + + if (i < adi->axes10) { + adi->dev.absmax[t] = x * 2 - 64; + adi->dev.absmin[t] = 64; + adi->dev.absfuzz[t] = 2; + adi->dev.absflat[t] = 16; + continue; + } + + if (i < adi->axes10 + adi->axes8) { + adi->dev.absmax[t] = x * 2 - 48; + adi->dev.absmin[t] = 48; + adi->dev.absfuzz[t] = 1; + adi->dev.absflat[t] = 16; + continue; + } + + adi->dev.absmax[t] = 1; + adi->dev.absmin[t] = -1; + } +} + +/* + * adi_connect() probes for Logitech ADI joysticks. + */ + +static void adi_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct adi_port *port; + int i; + + if (!(port = kmalloc(sizeof(struct adi_port), GFP_KERNEL))) + return; + memset(port, 0, sizeof(struct adi_port)); + + gameport->private = port; + + port->gameport = gameport; + init_timer(&port->timer); + port->timer.data = (long) port; + port->timer.function = adi_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { + kfree(port); + return; + } + + adi_init_digital(gameport); + adi_read_packet(port); + + if (port->adi[0].ret >= ADI_MIN_LEN_LENGTH) + adi_move_bits(port, adi_get_bits(port->adi, 10)); + + for (i = 0; i < 2; i++) { + adi_id_decode(port->adi + i, port); + adi_init_input(port->adi + i, port); + } + + if (!port->adi[0].length && !port->adi[1].length) { + gameport_close(gameport); + kfree(port); + return; + } + + wait_ms(ADI_INIT_DELAY); + if (adi_read(port)) { + wait_ms(ADI_DATA_DELAY); + adi_read(port); + } + + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) { + adi_init_center(port->adi + i); + input_register_device(&port->adi[i].dev); + printk(KERN_INFO "input%d: %s [%s] on gameport%d.%d\n", + port->adi[i].dev.number, port->adi[i].name, port->adi[i].cname, gameport->number, i); + } +} + +static void adi_disconnect(struct gameport *gameport) +{ + int i; + + struct adi_port *port = gameport->private; + for (i = 0; i < 2; i++) + if (port->adi[i].length > 0) + input_unregister_device(&port->adi[i].dev); + gameport_close(gameport); + kfree(port); +} + +/* + * The gameport device structure. + */ + +static struct gameport_dev adi_dev = { + connect: adi_connect, + disconnect: adi_disconnect, +}; + +int __init adi_init(void) +{ + gameport_register_device(&adi_dev); + return 0; +} + +void __exit adi_exit(void) +{ + gameport_unregister_device(&adi_dev); +} + +module_init(adi_init); +module_exit(adi_exit); diff --git a/drivers/char/joystick/amijoy.c b/drivers/char/joystick/amijoy.c new file mode 100644 index 000000000..f17aeda24 --- /dev/null +++ b/drivers/char/joystick/amijoy.c @@ -0,0 +1,149 @@ +/* + * $Id: amijoy.c,v 1.4 2000/05/29 10:39:54 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Driver for Amiga joysticks for Linux/m68k + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> + +#include <asm/system.h> +#include <asm/amigahw.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(amijoy, "1-2i"); + +static int amijoy[2] = { 0, 1 }; +static int amijoy_used[2] = { 0, 0 }; +static struct input_dev amijoy_dev[2]; + +static char *amijoy_name = "Amiga joystick"; + +static void amijoy_interrupt(int irq, void *dummy, struct pt_regs *fp) +{ + int i, data = 0, button = 0; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + + switch (i) { + case 0: data = ~custom.joy0dat; button = (~ciaa.pra >> 6) & 1; break; + case 1: data = ~custom.joy1dat; button = (~ciaa.pra >> 7) & 1; break; + } + + input_report_key(amijoy_dev + i, BTN_TRIGGER, button); + + input_report_abs(amijoy_dev + i, ABS_X, ((data >> 1) & 1) - ((data >> 9) & 1); + data = ~(data ^ (data << 1)); + input_report_abs(amijoy_dev + i, ABS_Y, ((data >> 1) & 1) - ((data >> 9) & 1); + } +} + +static int amijoy_open(struct input_dev *dev) +{ + int *used = dev->private; + + if ((*used)++) + return 0; + + if (request_irq(IRQ_AMIGA_VERTB, amijoy_interrupt, 0, "amijoy", NULL)) { + amijoy_used--; + printk(KERN_ERR "amijoy.c: Can't allocate irq %d\n", amijoy_irq); + return -EBUSY; + } + + return 0; +} + +static void amijoy_close(struct input_dev *dev) +{ + int *used = dev->private; + + if (!--(*port->used)) + free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); +} + +static int __init amijoy_setup(char *str) +{ + int i; + int ints[4] + str = get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) amijoy[i] = ints[i+1]; + return 1; +} +__setup("amijoy=", amijoy_setup); + +static int __init amijoy_init(void) +{ + int i, j; + + init_timer(amijoy_timer); + port->timer.function = amijoy_timer; + + for (i = 0; i < 2; i++) + if (amijoy[i]) { + + amijoy_dev[i].open = amijoy_open; + amijoy_dev[i].close = amijoy_close; + amijoy_dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + amijoy_dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + amijoy_dev[i].keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT); + for (j = 0; j < 2; j++) + amijoy_dev[i].absmin[ABS_X + j] = -1; + amijoy_dev[i].absmax[ABS_X + j] = 1; + } + + amijoy->dev[i].name = amijoy_name; + amijoy->dev[i].idbus = BUS_AMIGA; + amijoy->dev[i].idvendor = 0x0001; + amijoy->dev[i].idproduct = 0x0003; + amijoy->dev[i].version = 0x0100; + + amijoy_dev[i].private = amijoy_used + i; + + input_register_device(amijoy_dev + i); + printk(KERN_INFO "input%d: %s at joy%ddat\n", amijoy_dev[i].number, amijoy_name, i); + } +} + +static void _exit amijoy_exit(void) +{ + int i; + + for (i = 0; i < 2; i++) + if (amijoy[i]) + input_unregister_device(amijoy_dev + i); +} + +module_init(amijoy_init); +module_exit(amijoy_exit); diff --git a/drivers/char/joystick/analog.c b/drivers/char/joystick/analog.c new file mode 100644 index 000000000..6514bef9b --- /dev/null +++ b/drivers/char/joystick/analog.c @@ -0,0 +1,758 @@ +/* + * $Id: analog.c,v 1.52 2000/06/07 13:07:06 vojtech Exp $ + * + * Copyright (c) 1996-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Analog joystick and gamepad driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/config.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/bitops.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +/* + * Option parsing. + */ + +MODULE_PARM(js,"1-16s"); + +#define ANALOG_PORTS 16 + +static char *js[ANALOG_PORTS]; +static int analog_options[ANALOG_PORTS]; + +/* + * Times, feature definitions. + */ + +#define ANALOG_RUDDER 0x00004 +#define ANALOG_THROTTLE 0x00008 +#define ANALOG_AXES_STD 0x0000f +#define ANALOG_BTNS_STD 0x000f0 + +#define ANALOG_BTNS_CHF 0x00100 +#define ANALOG_HAT1_CHF 0x00200 +#define ANALOG_HAT2_CHF 0x00400 +#define ANALOG_HAT_FCS 0x00800 +#define ANALOG_HATS_ALL 0x00e00 +#define ANALOG_BTN_TL 0x01000 +#define ANALOG_BTN_TR 0x02000 +#define ANALOG_BTN_TL2 0x04000 +#define ANALOG_BTN_TR2 0x08000 +#define ANALOG_BTNS_TLR 0x03000 +#define ANALOG_BTNS_TLR2 0x0c000 +#define ANALOG_BTNS_GAMEPAD 0x0f000 + +#define ANALOG_HBTN_CHF 0x10000 +#define ANALOG_ANY_CHF 0x10700 +#define ANALOG_SAITEK 0x20000 +#define ANALOG_EXTENSIONS 0x7ff00 +#define ANALOG_GAMEPAD 0x80000 + +#define ANALOG_MAX_TIME 3 /* 3 ms */ +#define ANALOG_LOOP_TIME 2000 /* 2 * loop */ +#define ANALOG_REFRESH_TIME HZ/100 /* 10 ms */ +#define ANALOG_SAITEK_DELAY 200 /* 200 us */ +#define ANALOG_SAITEK_TIME 2000 /* 2000 us */ +#define ANALOG_AXIS_TIME 2 /* 2 * refresh */ +#define ANALOG_INIT_RETRIES 8 /* 8 times */ +#define ANALOG_FUZZ_BITS 2 /* 2 bit more */ +#define ANALOG_FUZZ_MAGIC 36 /* 36 u*ms/loop */ + +#define ANALOG_MAX_NAME_LENGTH 128 + +static short analog_axes[] = { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE }; +static short analog_hats[] = { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y }; +static short analog_pads[] = { BTN_Y, BTN_Z, BTN_TL, BTN_TR }; +static short analog_exts[] = { ANALOG_HAT1_CHF, ANALOG_HAT2_CHF, ANALOG_HAT_FCS }; +static short analog_pad_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_BASE }; +static short analog_joy_btn[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, + BTN_BASE3, BTN_BASE4, BTN_BASE5, BTN_BASE6 }; + +static unsigned char analog_chf[] = { 0xf, 0x0, 0x1, 0x9, 0x2, 0x4, 0xc, 0x8, 0x3, 0x5, 0xb, 0x7, 0xd, 0xe, 0xa, 0x6 }; + +struct analog { + struct input_dev dev; + int mask; + short *buttons; + char name[ANALOG_MAX_NAME_LENGTH]; +}; + +struct analog_port { + struct gameport *gameport; + struct timer_list timer; + struct analog analog[2]; + unsigned char mask; + char saitek; + char cooked; + int bads; + int reads; + int speed; + int loop; + int fuzz; + int axes[4]; + int buttons; + int initial[4]; + int used; + int axtime; +}; + +/* + * Time macros. + */ + +#ifdef __i386__ +#ifdef CONFIG_X86_TSC +#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "TSC" +#else +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((x)-(y)+((x)<(y)?1193180L/HZ:0)) +#define TIME_NAME "PIT" +#endif +#elif __alpha__ +#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "PCC" +#endif + +#ifndef GET_TIME +#define FAKE_TIME +static unsigned long analog_faketime = 0; +#define GET_TIME(x) do { x = analog_faketime++; } while(0) +#define DELTA(x,y) ((y)-(x)) +#define TIME_NAME "Unreliable" +#endif + +/* + * analog_decode() decodes analog joystick data and reports input events. + */ + +static void analog_decode(struct analog *analog, int *axes, int *initial, int buttons) +{ + struct input_dev *dev = &analog->dev; + int i, j; + + if (analog->mask & ANALOG_HAT_FCS) + for (i = 0; i < 4; i++) + if (axes[3] < ((initial[3] * ((i << 1) + 1)) >> 3)) { + buttons |= 1 << (i + 14); + break; + } + + for (i = j = 0; i < 6; i++) + if (analog->mask & (0x10 << i)) + input_report_key(dev, analog->buttons[j++], (buttons >> i) & 1); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + input_report_key(dev, analog->buttons[j++], (buttons >> (i + 10)) & 1); + + if (analog->mask & ANALOG_BTN_TL) + input_report_key(dev, analog_pads[0], axes[2] < (initial[2] >> 1)); + if (analog->mask & ANALOG_BTN_TR) + input_report_key(dev, analog_pads[1], axes[3] < (initial[3] >> 1)); + if (analog->mask & ANALOG_BTN_TL2) + input_report_key(dev, analog_pads[2], axes[2] > (initial[2] + (initial[2] >> 1))); + if (analog->mask & ANALOG_BTN_TR2) + input_report_key(dev, analog_pads[3], axes[3] > (initial[3] + (initial[3] >> 1))); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) + input_report_abs(dev, analog_axes[j++], axes[i]); + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) { + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 7)) & 1) - ((buttons >> ((i << 2) + 9)) & 1)); + input_report_abs(dev, analog_hats[j++], + ((buttons >> ((i << 2) + 8)) & 1) - ((buttons >> ((i << 2) + 6)) & 1)); + } +} + +/* + * analog_cooked_read() reads analog joystick data. + */ + +static int analog_cooked_read(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int time[4], start, loop, now, loopout, timeout; + unsigned char data[4], this, last; + unsigned long flags; + int i, j; + + loopout = (ANALOG_LOOP_TIME * port->loop) / 1000; + timeout = ANALOG_MAX_TIME * port->speed; + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + GET_TIME(now); + __restore_flags(flags); + + start = now; + this = port->mask; + i = 0; + + do { + loop = now; + last = this; + + __cli(); + this = gameport_read(gameport) & port->mask; + GET_TIME(now); + __restore_flags(flags); + + if ((last ^ this) && (DELTA(loop, now) < loopout)) { + data[i] = last ^ this; + time[i] = now; + i++; + } + + } while (this && (i < 4) && (DELTA(start, now) < timeout)); + + this <<= 4; + + for (--i; i >= 0; i--) { + this |= data[i]; + for (j = 0; j < 4; j++) + if (data[i] & (1 << j)) + port->axes[j] = (DELTA(start, time[i]) << ANALOG_FUZZ_BITS) / port->loop; + } + + return -(this != port->mask); +} + +static int analog_button_read(struct analog_port *port, char saitek, char chf) +{ + unsigned char u; + int t = 1, i = 0; + int strobe = gameport_time(port->gameport, ANALOG_SAITEK_TIME); + + u = gameport_read(port->gameport); + + if (!chf) { + port->buttons = (~u >> 4) & 0xf; + return 0; + } + + port->buttons = 0; + + while ((~u & 0xf0) && (i < 16) && t) { + port->buttons |= 1 << analog_chf[(~u >> 4) & 0xf]; + if (!saitek) return 0; + udelay(ANALOG_SAITEK_DELAY); + t = strobe; + gameport_trigger(port->gameport); + while (((u = gameport_read(port->gameport)) & port->mask) && t) t--; + i++; + } + + return -(!t || (i == 16)); +} + +/* + * analog_timer() repeatedly polls the Analog joysticks. + */ + +static void analog_timer(unsigned long data) +{ + struct analog_port *port = (void *) data; + int i; + + char saitek = !!(port->analog[0].mask & ANALOG_SAITEK); + char chf = !!(port->analog[0].mask & ANALOG_ANY_CHF); + + if (port->cooked) { + port->bads -= gameport_cooked_read(port->gameport, port->axes, &port->buttons); + if (chf) + port->buttons = port->buttons ? (1 << analog_chf[port->buttons]) : 0; + port->reads++; + } else { + if (!port->axtime--) { + port->bads -= analog_cooked_read(port); + port->bads -= analog_button_read(port, saitek, chf); + port->reads++; + port->axtime = ANALOG_AXIS_TIME - 1; + } else { + if (!saitek) + analog_button_read(port, saitek, chf); + } + } + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + analog_decode(port->analog + i, port->axes, port->initial, port->buttons); + + mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); +} + +/* + * analog_open() is a callback from the input open routine. + */ + +static int analog_open(struct input_dev *dev) +{ + struct analog_port *port = dev->private; + if (!port->used++) + mod_timer(&port->timer, jiffies + ANALOG_REFRESH_TIME); + return 0; +} + +/* + * analog_close() is a callback from the input close routine. + */ + +static void analog_close(struct input_dev *dev) +{ + struct analog_port *port = dev->private; + if (!--port->used) + del_timer(&port->timer); +} + +/* + * analog_calibrate_timer() calibrates the timer and computes loop + * and timeout values for a joystick port. + */ + +static void analog_calibrate_timer(struct analog_port *port) +{ + struct gameport *gameport = port->gameport; + unsigned int i, t, tx, t1, t2, t3; + unsigned long flags; + + save_flags(flags); + cli(); + GET_TIME(t1); +#ifdef FAKE_TIME + analog_faketime += 830; +#endif + udelay(1000); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + + port->speed = DELTA(t1, t2) - DELTA(t2, t3); + + tx = ~0; + + for (i = 0; i < 50; i++) { + save_flags(flags); + cli(); + GET_TIME(t1); + for (t = 0; t < 50; t++) { gameport_read(gameport); GET_TIME(t2); } + GET_TIME(t3); + restore_flags(flags); + udelay(i); + t = DELTA(t1, t2) - DELTA(t2, t3); + if (t < tx) tx = t; + } + + port->loop = tx / 50; +} + +/* + * analog_name() constructs a name for an analog joystick. + */ + +static void analog_name(struct analog *analog) +{ + sprintf(analog->name, "Analog %d-axis %d-button", + hweight8(analog->mask & ANALOG_AXES_STD), + hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 + + hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4); + + if (analog->mask & ANALOG_HATS_ALL) + sprintf(analog->name, "%s %d-hat", + analog->name, hweight16(analog->mask & ANALOG_HATS_ALL)); + + if (analog->mask & ANALOG_HAT_FCS) + strcat(analog->name, " FCS"); + if (analog->mask & ANALOG_ANY_CHF) + strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF"); + + strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick"); +} + +/* + * analog_init_device() + */ + +static void analog_init_device(struct analog_port *port, struct analog *analog, int index) +{ + int i, j, t, v, w, x, y, z; + + analog_name(analog); + + analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn; + + analog->dev.name = analog->name; + analog->dev.idbus = BUS_GAMEPORT; + analog->dev.idvendor = GAMEPORT_ID_VENDOR_ANALOG; + analog->dev.idproduct = analog->mask >> 4; + analog->dev.idversion = 0x0100; + + analog->dev.open = analog_open; + analog->dev.close = analog_close; + analog->dev.private = port; + analog->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = j = 0; i < 4; i++) + if (analog->mask & (1 << i)) { + + t = analog_axes[j]; + x = port->axes[i]; + y = (port->axes[0] + port->axes[1]) >> 1; + z = y - port->axes[i]; + z = z > 0 ? z : -z; + v = (x >> 3); + w = (x >> 3); + + set_bit(t, analog->dev.absbit); + + if ((i == 2 || i == 3) && (j == 2 || j == 3) && (z > (y >> 3))) + x = y; + + if (analog->mask & ANALOG_SAITEK) { + if (i == 2) x = port->axes[i]; + v = x - (x >> 2); + w = (x >> 4); + } + + analog->dev.absmax[t] = (x << 1) - v; + analog->dev.absmin[t] = v; + analog->dev.absfuzz[t] = port->fuzz; + analog->dev.absflat[t] = w; + + j++; + } + + for (i = j = 0; i < 3; i++) + if (analog->mask & analog_exts[i]) + for (x = 0; x < 2; x++) { + t = analog_hats[j++]; + set_bit(t, analog->dev.absbit); + analog->dev.absmax[t] = 1; + analog->dev.absmin[t] = -1; + } + + for (i = j = 0; i < 4; i++) + if (analog->mask & (0x10 << i)) + set_bit(analog->buttons[j++], analog->dev.keybit); + + if (analog->mask & ANALOG_BTNS_CHF) + for (i = 0; i < 2; i++) + set_bit(analog->buttons[j++], analog->dev.keybit); + + if (analog->mask & ANALOG_HBTN_CHF) + for (i = 0; i < 4; i++) + set_bit(analog->buttons[j++], analog->dev.keybit); + + for (i = 0; i < 4; i++) + if (analog->mask & (ANALOG_BTN_TL << i)) + set_bit(analog_pads[i], analog->dev.keybit); + + analog_decode(analog, port->axes, port->initial, port->buttons); + + input_register_device(&analog->dev); + + printk(KERN_INFO "input%d: %s at gameport%d.%d", + analog->dev.number, analog->name, port->gameport->number, index); + + if (port->cooked) + printk(" [ADC port]\n"); + else + printk(" ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", + port->speed > 10000 ? (port->speed + 800) / 1000 : port->speed, + port->speed > 10000 ? "M" : "k", (port->loop * 1000000) / port->speed); +} + +/* + * analog_init_devices() sets up device-specific values and registers the input devices. + */ + +static int analog_init_masks(struct analog_port *port) +{ + int i; + struct analog *analog = port->analog; + int max[4]; + + if (!port->mask) + return -1; + + if ((port->mask & 3) != 3 && port->mask != 0xc) { + printk(KERN_WARNING "analog.c: Unknown joystick device found " + "(data=%#x, gameport%d), probably not analog joystick.\n", + port->mask, port->gameport->number); + return -1; + } + + i = port->gameport->number < ANALOG_PORTS ? analog_options[port->gameport->number] : 0xff; + + analog[0].mask = i & 0xfffff; + + analog[0].mask &= ~(ANALOG_AXES_STD | ANALOG_HAT_FCS | ANALOG_BTNS_GAMEPAD) + | port->mask | ((port->mask << 8) & ANALOG_HAT_FCS) + | ((port->mask << 10) & ANALOG_BTNS_TLR) | ((port->mask << 12) & ANALOG_BTNS_TLR2); + + analog[0].mask &= ~(ANALOG_HAT2_CHF) + | ((analog[0].mask & ANALOG_HBTN_CHF) ? 0 : ANALOG_HAT2_CHF); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_BTN_TR | ANALOG_BTN_TR2) + | ((~analog[0].mask & ANALOG_HAT_FCS) >> 8) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 2) + | ((~analog[0].mask & ANALOG_HAT_FCS) << 4); + + analog[0].mask &= ~(ANALOG_THROTTLE | ANALOG_RUDDER) + | (((~analog[0].mask & ANALOG_BTNS_TLR ) >> 10) + & ((~analog[0].mask & ANALOG_BTNS_TLR2) >> 12)); + + analog[1].mask = ((i >> 20) & 0xff) | ((i >> 12) & 0xf0000); + + analog[1].mask &= (analog[0].mask & ANALOG_EXTENSIONS) ? ANALOG_GAMEPAD + : (((ANALOG_BTNS_STD | port->mask) & ~analog[0].mask) | ANALOG_GAMEPAD); + + if (port->cooked) { + + for (i = 0; i < 4; i++) max[i] = port->axes[i] << 1; + + if ((analog[0].mask & 0x7) == 0x7) max[2] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & 0xb) == 0xb) max[3] = (max[0] + max[1]) >> 1; + if ((analog[0].mask & ANALOG_BTN_TL) && !(analog[0].mask & ANALOG_BTN_TL2)) max[2] >>= 1; + if ((analog[0].mask & ANALOG_BTN_TR) && !(analog[0].mask & ANALOG_BTN_TR2)) max[3] >>= 1; + if ((analog[0].mask & ANALOG_HAT_FCS)) max[3] >>= 1; + + gameport_calibrate(port->gameport, port->axes, max); + } + + for (i = 0; i < 4; i++) + port->initial[i] = port->axes[i]; + + return -!(analog[0].mask || analog[1].mask); +} + +static int analog_init_port(struct gameport *gameport, struct gameport_dev *dev, struct analog_port *port) +{ + int i, t, u, v; + + gameport->private = port; + port->gameport = gameport; + init_timer(&port->timer); + port->timer.data = (long) port; + port->timer.function = analog_timer; + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) { + + analog_calibrate_timer(port); + + gameport_trigger(gameport); + t = gameport_read(gameport); + wait_ms(ANALOG_MAX_TIME); + port->mask = (gameport_read(gameport) ^ t) & t & 0xf; + port->fuzz = (port->speed * ANALOG_FUZZ_MAGIC) / port->loop / 1000 + ANALOG_FUZZ_BITS; + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) { + if (!analog_cooked_read(port)) break; + wait_ms(ANALOG_MAX_TIME); + } + + u = v = 0; + + wait_ms(ANALOG_MAX_TIME); + t = gameport_time(gameport, ANALOG_MAX_TIME * 1000); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (u < t)) u++; + udelay(ANALOG_SAITEK_DELAY); + t = gameport_time(gameport, ANALOG_SAITEK_TIME); + gameport_trigger(gameport); + while ((gameport_read(port->gameport) & port->mask) && (v < t)) v++; + + if (v < (u >> 1) && port->gameport->number < ANALOG_PORTS) { + analog_options[port->gameport->number] |= + ANALOG_SAITEK | ANALOG_BTNS_CHF | ANALOG_HBTN_CHF | ANALOG_HAT1_CHF; + return 0; + } + + gameport_close(gameport); + } + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_COOKED)) { + + for (i = 0; i < ANALOG_INIT_RETRIES; i++) + if (!gameport_cooked_read(gameport, port->axes, &port->buttons)) + break; + for (i = 0; i < 4; i++) + if (port->axes[i] != -1) port->mask |= 1 << i; + + port->fuzz = gameport->fuzz; + port->cooked = 1; + return 0; + } + + if (!gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + return 0; + + return -1; +} + +static void analog_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct analog_port *port; + int i; + + if (!(port = kmalloc(sizeof(struct analog_port), GFP_KERNEL))) + return; + memset(port, 0, sizeof(struct analog_port)); + + if (analog_init_port(gameport, dev, port)) { + kfree(port); + return; + } + + if (analog_init_masks(port)) { + gameport_close(gameport); + kfree(port); + return; + } + + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + analog_init_device(port, port->analog + i, i); +} + +static void analog_disconnect(struct gameport *gameport) +{ + int i; + + struct analog_port *port = gameport->private; + for (i = 0; i < 2; i++) + if (port->analog[i].mask) + input_unregister_device(&port->analog[i].dev); + gameport_close(gameport); + printk(KERN_INFO "analog.c: %d out of %d reads (%d%%) on gameport%d failed\n", + port->bads, port->reads, port->reads ? (port->bads * 100 / port->reads) : 0, + port->gameport->number); + kfree(port); +} + +struct analog_types { + char *name; + int value; +}; + +struct analog_types analog_types[] = { + { "none", 0x00000000 }, + { "auto", 0x000000ff }, + { "2btn", 0x0000003f }, + { "y-joy", 0x0cc00033 }, + { "y-pad", 0x8cc80033 }, + { "fcs", 0x000008f7 }, + { "chf", 0x000002ff }, + { "fullchf", 0x000007ff }, + { "gamepad", 0x000830f3 }, + { "gamepad8", 0x0008f0f3 }, + { NULL, 0 } +}; + +static void analog_parse_options(void) +{ + int i, j; + char *end; + + for (i = 0; i < ANALOG_PORTS && js[i]; i++) { + + for (j = 0; analog_types[j].name; j++) + if (!strcmp(analog_types[j].name, js[i])) { + analog_options[i] = analog_types[j].value; + break; + } + if (analog_types[j].name) continue; + + analog_options[i] = simple_strtoul(js[i], &end, 0); + if (end != js[i]) continue; + + analog_options[i] = 0xff; + if (!strlen(js[i])) continue; + + printk(KERN_WARNING "analog.c: Bad config for port %d - \"%s\"\n", i, js[i]); + } + + for (; i < ANALOG_PORTS; i++) + analog_options[i] = 0xff; +} + +/* + * The gameport device structure. + */ + +static struct gameport_dev analog_dev = { + connect: analog_connect, + disconnect: analog_disconnect, +}; + +#ifndef MODULE +static int __init analog_setup(char *str) +{ + char *s = str; + int i = 0; + + if (!str || !*str) return 0; + + while ((str = s) && (i < ANALOG_PORTS)) { + if ((s = strchr(str,','))) *s++ = 0; + js[i++] = str; + } + + return 1; +} +__setup("js=", analog_setup); +#endif + +int __init analog_init(void) +{ + analog_parse_options(); + gameport_register_device(&analog_dev); + return 0; +} + +void __exit analog_exit(void) +{ + gameport_unregister_device(&analog_dev); +} + +module_init(analog_init); +module_exit(analog_exit); diff --git a/drivers/char/joystick/cobra.c b/drivers/char/joystick/cobra.c new file mode 100644 index 000000000..f059a2ff6 --- /dev/null +++ b/drivers/char/joystick/cobra.c @@ -0,0 +1,250 @@ +/* + * $Id: cobra.c,v 1.10 2000/06/08 10:23:45 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Creative Labd Blaster GamePad Cobra driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define COBRA_MAX_STROBE 45 /* 45 us max wait for first strobe */ +#define COBRA_REFRESH_TIME HZ/50 /* 20 ms between reads */ +#define COBRA_LENGTH 36 + +static char* cobra_name = "Creative Labs Blaster GamePad Cobra"; + +static int cobra_btn[] = { BTN_START, BTN_SELECT, BTN_TL, BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL2, BTN_TR2, 0 }; + +struct cobra { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + int used; + int reads; + int bads; + unsigned char exists; +}; + +static unsigned char cobra_read_packet(struct gameport *gameport, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v, w; + __u64 buf[2]; + int r[2], t[2]; + int i, j, ret; + + int strobe = gameport_time(gameport, COBRA_MAX_STROBE); + + for (i = 0; i < 2; i++) { + r[i] = buf[i] = 0; + t[i] = COBRA_MAX_STROBE; + } + + __save_flags(flags); + __cli(); + + u = gameport_read(gameport); + + do { + t[0]--; t[1]--; + v = gameport_read(gameport); + for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) + if (w & 0x30) { + if ((w & 0x30) < 0x30 && r[i] < COBRA_LENGTH && t[i] > 0) { + buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; + t[i] = strobe; + u = v; + } else t[i] = 0; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + ret = 0; + + for (i = 0; i < 2; i++) { + + if (r[i] != COBRA_LENGTH) continue; + + for (j = 0; j < COBRA_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) + buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (COBRA_LENGTH - 1)); + + if (j < COBRA_LENGTH) ret |= (1 << i); + + data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) + | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) + | ((buf[i] >> 11) & 0x1f00000); + + } + + return ret; +} + +static void cobra_timer(unsigned long private) +{ + struct cobra *cobra = (void *) private; + struct input_dev *dev; + unsigned int data[2]; + int i, j, r; + + cobra->reads++; + + if ((r = cobra_read_packet(cobra->gameport, data)) != cobra->exists) + cobra->bads++; + + for (i = 0; i < 2; i++) + if (cobra->exists & r & (1 << i)) { + + dev = cobra->dev + i; + + input_report_abs(dev, ABS_X, ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1)); + input_report_abs(dev, ABS_Y, ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1)); + + for (j = 0; cobra_btn[j]; j++) + input_report_key(dev, cobra_btn[j], data[i] & (0x20 << i)); + + } + + mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME); +} + +static int cobra_open(struct input_dev *dev) +{ + struct cobra *cobra = dev->private; + if (!cobra->used++) + mod_timer(&cobra->timer, jiffies + COBRA_REFRESH_TIME); + return 0; +} + +static void cobra_close(struct input_dev *dev) +{ + struct cobra *cobra = dev->private; + if (!--cobra->used) + del_timer(&cobra->timer); +} + +static void cobra_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct cobra *cobra; + unsigned int data[2]; + int i, j; + + if (!(cobra = kmalloc(sizeof(struct cobra), GFP_KERNEL))) + return; + memset(cobra, 0, sizeof(struct cobra)); + + gameport->private = cobra; + + cobra->gameport = gameport; + init_timer(&cobra->timer); + cobra->timer.data = (long) cobra; + cobra->timer.function = cobra_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + cobra->exists = cobra_read_packet(gameport, data); + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & data[i] & 1) { + printk(KERN_WARNING "cobra.c: Device on gameport%d.%d has the Ext bit set. ID is: %d" + " Contact vojtech@suse.cz\n", gameport->number, i, (data[i] >> 2) & 7); + cobra->exists &= ~(1 << i); + } + + if (!cobra->exists) + goto fail2; + + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & 1) { + + cobra->dev[i].private = cobra; + cobra->dev[i].open = cobra_open; + cobra->dev[i].close = cobra_close; + + cobra->dev[i].name = cobra_name; + cobra->dev[i].idbus = BUS_GAMEPORT; + cobra->dev[i].idvendor = GAMEPORT_ID_VENDOR_CREATIVE; + cobra->dev[i].idproduct = 0x0008; + cobra->dev[i].idversion = 0x0100; + + cobra->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + cobra->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; cobra_btn[j]; j++) + set_bit(cobra_btn[j], cobra->dev[i].keybit); + + cobra->dev[i].absmin[ABS_X] = -1; cobra->dev[i].absmax[ABS_X] = 1; + cobra->dev[i].absmin[ABS_Y] = -1; cobra->dev[i].absmax[ABS_Y] = 1; + + input_register_device(cobra->dev + i); + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + cobra->dev[i].number, cobra_name, gameport->number, i); + } + + + return; +fail2: gameport_close(gameport); +fail1: kfree(cobra); +} + +static void cobra_disconnect(struct gameport *gameport) +{ + int i; + + struct cobra *cobra = gameport->private; + for (i = 0; i < 2; i++) + if ((cobra->exists >> i) & 1) + input_unregister_device(cobra->dev + i); + gameport_close(gameport); + kfree(cobra); +} + +static struct gameport_dev cobra_dev = { + connect: cobra_connect, + disconnect: cobra_disconnect, +}; + +int __init cobra_init(void) +{ + gameport_register_device(&cobra_dev); + return 0; +} + +void __exit cobra_exit(void) +{ + gameport_unregister_device(&cobra_dev); +} + +module_init(cobra_init); +module_exit(cobra_exit); diff --git a/drivers/char/joystick/db9.c b/drivers/char/joystick/db9.c new file mode 100644 index 000000000..f9edd0755 --- /dev/null +++ b/drivers/char/joystick/db9.c @@ -0,0 +1,423 @@ +/* + * $Id: db9.c,v 1.5 2000/05/29 20:39:38 vojtech Exp $ + * + * Copyright (c) 1999 Vojtech Pavlik + * + * Based on the work of: + * Andree Borrmann Mats Sjövall + * + * Sponsored by SuSE + */ + +/* + * Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/parport.h> +#include <linux/input.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(db9, "2i"); +MODULE_PARM(db9_2, "2i"); +MODULE_PARM(db9_3, "2i"); + +#define DB9_MULTI_STICK 0x01 +#define DB9_MULTI2_STICK 0x02 +#define DB9_GENESIS_PAD 0x03 +#define DB9_GENESIS5_PAD 0x05 +#define DB9_GENESIS6_PAD 0x06 +#define DB9_SATURN_PAD 0x07 +#define DB9_MULTI_0802 0x08 +#define DB9_MULTI_0802_2 0x09 +#define DB9_CD32_PAD 0x0A +#define DB9_MAX_PAD 0x0B + +#define DB9_UP 0x01 +#define DB9_DOWN 0x02 +#define DB9_LEFT 0x04 +#define DB9_RIGHT 0x08 +#define DB9_FIRE1 0x10 +#define DB9_FIRE2 0x20 +#define DB9_FIRE3 0x40 +#define DB9_FIRE4 0x80 + +#define DB9_NORMAL 0x0a +#define DB9_NOSELECT 0x08 + +#define DB9_SATURN0 0x00 +#define DB9_SATURN1 0x02 +#define DB9_SATURN2 0x04 +#define DB9_SATURN3 0x06 + +#define DB9_GENESIS6_DELAY 14 +#define DB9_REFRESH_TIME HZ/100 + +static int db9[] __initdata = { -1, 0 }; +static int db9_2[] __initdata = { -1, 0 }; +static int db9_3[] __initdata = { -1, 0 }; + +struct db9 { + struct input_dev dev[2]; + struct timer_list timer; + struct pardevice *pd; + int mode; + int used; +}; + +static struct db9 *db9_base[3]; + +static short db9_multi_btn[] = { BTN_TRIGGER, BTN_THUMB }; +static short db9_genesis_btn[] = { BTN_START, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_MODE }; +static short db9_cd32_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START }; + +static char db9_buttons[DB9_MAX_PAD] = { 0, 1, 2, 4, 0, 6, 8, 8, 1, 1, 7 }; +static short *db9_btn[DB9_MAX_PAD] = { db9_multi_btn, db9_multi_btn, db9_genesis_btn, NULL, db9_genesis_btn, + db9_genesis_btn, db9_cd32_btn, db9_multi_btn, db9_multi_btn, db9_cd32_btn }; +static char *db9_name[DB9_MAX_PAD] = { NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", + NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", + "Multisystem (0.8.0.2-dual) joystick", "Amiga CD-32 pad" }; + +static void db9_timer(unsigned long private) +{ + struct db9 *db9 = (void *) private; + struct parport *port = db9->pd->port; + struct input_dev *dev = db9->dev; + int data, i; + + switch(db9->mode) { + case DB9_MULTI_0802_2: + + data = parport_read_data(port) >> 3; + + input_report_abs(dev + 1, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev + 1, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev + 1, BTN_TRIGGER, ~data&DB9_FIRE1); + + case DB9_MULTI_0802: + + data = parport_read_status(port) >> 3; + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, data&DB9_FIRE1); + break; + + case DB9_MULTI_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, ~data&DB9_FIRE1); + break; + + case DB9_MULTI2_STICK: + + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_TRIGGER, ~data&DB9_FIRE1); + input_report_key(dev, BTN_THUMB, ~data&DB9_FIRE2); + break; + + case DB9_GENESIS_PAD: + + parport_write_control(port, DB9_NOSELECT); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_START, ~data&DB9_FIRE2); + break; + + case DB9_GENESIS5_PAD: + + parport_write_control(port, DB9_NOSELECT); + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_X, ~data&DB9_FIRE2); + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_START, ~data&DB9_RIGHT); + break; + + case DB9_GENESIS6_PAD: + + parport_write_control(port, DB9_NOSELECT); /* 1 */ + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + input_report_key(dev, BTN_B, ~data&DB9_FIRE1); + input_report_key(dev, BTN_C, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_FIRE1); + input_report_key(dev, BTN_X, ~data&DB9_FIRE2); + + parport_write_control(port, DB9_NOSELECT); /* 2 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 3 */ + udelay(DB9_GENESIS6_DELAY); + data=parport_read_data(port); + + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_Z, ~data&DB9_DOWN); + input_report_key(dev, BTN_MODE, ~data&DB9_UP); + input_report_key(dev, BTN_START, ~data&DB9_RIGHT); + + parport_write_control(port, DB9_NORMAL); + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NOSELECT); /* 4 */ + udelay(DB9_GENESIS6_DELAY); + parport_write_control(port, DB9_NORMAL); + break; + + case DB9_SATURN_PAD: + + parport_write_control(port, DB9_SATURN0); + data = parport_read_data(port); + + input_report_key(dev, BTN_Y, ~data&DB9_LEFT); + input_report_key(dev, BTN_Z, ~data&DB9_DOWN); + input_report_key(dev, BTN_TL,~data&DB9_UP); + input_report_key(dev, BTN_TR,~data&DB9_RIGHT); + + parport_write_control(port, DB9_SATURN2); + data = parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + + parport_write_control(port, DB9_NORMAL); + data = parport_read_data(port); + + input_report_key(dev, BTN_A, ~data&DB9_LEFT); + input_report_key(dev, BTN_B, ~data&DB9_UP); + input_report_key(dev, BTN_C, ~data&DB9_DOWN); + input_report_key(dev, BTN_X, ~data&DB9_RIGHT); + break; + + case DB9_CD32_PAD: + + data=parport_read_data(port); + + input_report_abs(dev, ABS_X, (data&DB9_DOWN ?0:1) - (data&DB9_UP ?0:1)); + input_report_abs(dev, ABS_Y, (data&DB9_RIGHT?0:1) - (data&DB9_LEFT?0:1)); + + parport_write_control(port, 0x0a); + + for (i = 0; i < 7; i++) { + data = parport_read_data(port); + parport_write_control(port, 0x02); + parport_write_control(port, 0x0a); + input_report_key(dev, db9_cd32_btn[i], ~data&DB9_FIRE2); + } + + parport_write_control(port, 0x00); + break; + } + + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); +} + +static int db9_open(struct input_dev *dev) +{ + struct db9 *db9 = dev->private; + struct parport *port = db9->pd->port; + + if (!db9->used++) { + parport_claim(db9->pd); + parport_write_data(port, 0xff); + parport_data_reverse(port); + parport_write_control(port, DB9_NORMAL); + mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); + } + + return 0; +} + +static void db9_close(struct input_dev *dev) +{ + struct db9 *db9 = dev->private; + struct parport *port = db9->pd->port; + + if (!--db9->used) { + del_timer(&db9->timer); + parport_write_control(port, 0x00); + parport_data_forward(port); + parport_release(db9->pd); + } +} + +static struct db9 __init *db9_probe(int *config) +{ + struct db9 *db9; + struct parport *pp; + int i, j; + + if (config[0] < 0) + return NULL; + if (config[1] < 1 || config[1] >= DB9_MAX_PAD || !db9_buttons[config[1]]) { + printk(KERN_ERR "db9.c: bad config\n"); + return NULL; + } + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "db9.c: no such parport\n"); + return NULL; + } + + if (!(pp->modes & PARPORT_MODE_TRISTATE) && config[1] != DB9_MULTI_0802) { + printk(KERN_ERR "db9.c: specified parport is not bidirectional\n"); + return NULL; + } + + if (!(db9 = kmalloc(sizeof(struct db9), GFP_KERNEL))) + return NULL; + memset(db9, 0, sizeof(struct db9)); + + db9->mode = config[1]; + init_timer(&db9->timer); + db9->timer.data = (long) db9; + db9->timer.function = db9_timer; + + db9->pd = parport_register_device(pp, "db9", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!db9->pd) { + printk(KERN_ERR "db9.c: parport busy already - lp.o loaded?\n"); + kfree(db9); + return NULL; + } + + for (i = 0; i < 1 + (db9->mode == DB9_MULTI_0802_2); i++) { + + db9->dev[i].private = db9; + db9->dev[i].open = db9_open; + db9->dev[i].close = db9_close; + + db9->dev[i].name = db9_name[db9->mode]; + db9->dev[i].idbus = BUS_PARPORT; + db9->dev[i].idvendor = 0x0002; + db9->dev[i].idproduct = config[1]; + db9->dev[i].idversion = 0x0100; + + db9->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + db9->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; j < db9_buttons[db9->mode]; j++) + set_bit(db9_btn[db9->mode][j], db9->dev[i].keybit); + + db9->dev[i].absmin[ABS_X] = -1; db9->dev[i].absmax[ABS_X] = 1; + db9->dev[i].absmin[ABS_Y] = -1; db9->dev[i].absmax[ABS_Y] = 1; + + input_register_device(db9->dev + i); + printk(KERN_INFO "input%d: %s on %s\n", + db9->dev[i].number, db9_name[db9->mode], db9->pd->port->name); + } + + return db9; +} + +#ifndef MODULE +int __init db9_setup(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9[i] = ints[i + 1]; + return 1; +} +int __init db9_setup_2(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9_2[i] = ints[i + 1]; + return 1; +} +int __init db9_setup_3(char *str) +{ + int i, ints[3]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 2; i++) db9_3[i] = ints[i + 1]; + return 1; +} +__setup("db9=", db9_setup); +__setup("db9_2=", db9_setup_2); +__setup("db9_3=", db9_setup_3); +#endif + +int __init db9_init(void) +{ + db9_base[0] = db9_probe(db9); + db9_base[1] = db9_probe(db9_2); + db9_base[2] = db9_probe(db9_3); + + if (db9_base[0] || db9_base[1] || db9_base[2]) + return 0; + + return -ENODEV; +} + +void __exit db9_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) + if (db9_base[i]) { + for (j = 0; j < 1 + (db9_base[i]->mode == DB9_MULTI_0802_2); j++) + input_unregister_device(db9_base[i]->dev + j); + parport_unregister_device(db9_base[i]->pd); + } +} + +module_init(db9_init); +module_exit(db9_exit); diff --git a/drivers/char/joystick/gamecon.c b/drivers/char/joystick/gamecon.c new file mode 100644 index 000000000..a92ef58a9 --- /dev/null +++ b/drivers/char/joystick/gamecon.c @@ -0,0 +1,668 @@ +/* + * $Id: gamecon.c,v 1.4 2000/05/29 21:08:45 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * Andree Borrmann John Dahlstrom + * David Kuder + * + * Sponsored by SuSE + */ + +/* + * NES, SNES, N64, Multi1, Multi2, PSX gamepad driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/parport.h> +#include <linux/input.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(gc, "2-6i"); +MODULE_PARM(gc_2,"2-6i"); +MODULE_PARM(gc_3,"2-6i"); + +#define GC_SNES 1 +#define GC_NES 2 +#define GC_NES4 3 +#define GC_MULTI 4 +#define GC_MULTI2 5 +#define GC_N64 6 +#define GC_PSX 7 + +#define GC_MAX 7 + +#define GC_REFRESH_TIME HZ/100 + +struct gc { + struct pardevice *pd; + struct input_dev dev[5]; + struct timer_list timer; + unsigned char pads[GC_PSX + 1]; + int used; +}; + +static struct gc *gc_base[3]; + +static int gc[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; +static int gc_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; + +static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; + +static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", + "Multisystem 2-button joystick", "N64 controller", "PSX pad", + "PSX NegCon", "PSX Analog contoller" }; +/* + * N64 support. + */ + +static unsigned char gc_n64_bytes[] = { 0, 1, 13, 15, 14, 12, 10, 11, 2, 3 }; +static short gc_n64_btn[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TRIGGER, BTN_START }; + +#define GC_N64_LENGTH 32 /* N64 bit length, not including stop bit */ +#define GC_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ +#define GC_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ +#define GC_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ +#define GC_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ + /* GC_N64_DWS > 24 is known to fail */ +#define GC_N64_POWER_W 0xe2 /* power during write (transmit request) */ +#define GC_N64_POWER_R 0xfd /* power during read */ +#define GC_N64_OUT 0x1d /* output bits to the 4 pads */ + /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ + /* in GC_N64_OUT is pulled low on the output port (by any routine) for more */ + /* than 123 us */ +#define GC_N64_CLOCK 0x02 /* clock bits for read */ + +/* + * gc_n64_read_packet() reads an N64 packet. + * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. + */ + +static void gc_n64_read_packet(struct gc *gc, unsigned char *data) +{ + int i; + unsigned long flags; + +/* + * Request the pad to transmit data + */ + + __save_flags(flags); + __cli(); + for (i = 0; i < GC_N64_REQUEST_LENGTH; i++) { + parport_write_data(gc->pd->port, GC_N64_POWER_W | ((GC_N64_REQUEST >> i) & 1 ? GC_N64_OUT : 0)); + udelay(GC_N64_DWS); + } + __restore_flags(flags); + +/* + * Wait for the pad response to be loaded into the 33-bit register of the adapter + */ + + udelay(GC_N64_DELAY); + +/* + * Grab data (ignoring the last bit, which is a stop bit) + */ + + for (i = 0; i < GC_N64_LENGTH; i++) { + parport_write_data(gc->pd->port, GC_N64_POWER_R); + data[i] = parport_read_status(gc->pd->port); + parport_write_data(gc->pd->port, GC_N64_POWER_R | GC_N64_CLOCK); + } + +/* + * We must wait 200 ms here for the controller to reinitialize before the next read request. + * No worries as long as gc_read is polled less frequently than this. + */ + +} + +/* + * NES/SNES support. + */ + +#define GC_NES_DELAY 6 /* Delay between bits - 6us */ +#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */ +#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ + +#define GC_NES_POWER 0xfc +#define GC_NES_CLOCK 0x01 +#define GC_NES_LATCH 0x02 + +static unsigned char gc_nes_bytes[] = { 0, 1, 2, 3 }; +static unsigned char gc_snes_bytes[] = { 8, 0, 2, 3, 9, 1, 10, 11 }; +static short gc_snes_btn[] = { BTN_A, BTN_B, BTN_START, BTN_SELECT, BTN_X, BTN_Y, BTN_TL, BTN_TR }; + +/* + * gc_nes_read_packet() reads a NES/SNES packet. + * Each pad uses one bit per byte. So all pads connected to + * this port are read in parallel. + */ + +static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK | GC_NES_LATCH); + udelay(GC_NES_DELAY * 2); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + + for (i = 0; i < length; i++) { + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + udelay(GC_NES_DELAY); + parport_write_data(gc->pd->port, GC_NES_POWER | GC_NES_CLOCK); + } +} + +/* + * Multisystem joystick support + */ + +#define GC_MULTI_LENGTH 5 /* Multi system joystick packet length is 5 */ +#define GC_MULTI2_LENGTH 6 /* One more bit for one more button */ + +/* + * gc_multi_read_packet() reads a Multisystem joystick packet. + */ + +static void gc_multi_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i; + + for (i = 0; i < length; i++) { + parport_write_data(gc->pd->port, ~(1 << i)); + data[i] = parport_read_status(gc->pd->port) ^ 0x7f; + } +} + +/* + * PSX support + */ + +#define GC_PSX_DELAY 10 +#define GC_PSX_LENGTH 8 /* talk to the controller in bytes */ + +#define GC_PSX_MOUSE 0x12 /* PSX Mouse */ +#define GC_PSX_NEGCON 0x23 /* NegCon pad */ +#define GC_PSX_NORMAL 0x41 /* Standard Digital controller */ +#define GC_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ +#define GC_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ + +#define GC_PSX_CLOCK 0x04 /* Pin 3 */ +#define GC_PSX_COMMAND 0x01 /* Pin 1 */ +#define GC_PSX_POWER 0xf8 /* Pins 5-9 */ +#define GC_PSX_SELECT 0x02 /* Pin 2 */ +#define GC_PSX_NOPOWER 0x04 + +static short gc_psx_abs[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_HAT0X, ABS_HAT0Y }; +static short gc_psx_btn[] = { BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_A, BTN_B, BTN_X, BTN_Y, + BTN_START, BTN_SELECT, BTN_THUMB, BTN_THUMB2 }; + +/* + * gc_psx_command() writes 8bit command and reads 8bit data from + * the psx pad. + */ + +static int gc_psx_command(struct gc *gc, int b) +{ + int i, cmd, ret = 0; + + cmd = (b & 1) ? GC_PSX_COMMAND : 0; + for (i = 0; i < 8; i++) { + parport_write_data(gc->pd->port, cmd | GC_PSX_POWER); + udelay(GC_PSX_DELAY); + ret |= ((parport_read_status(gc->pd->port) ^ 0x80) & gc->pads[GC_PSX]) ? (1 << i) : 0; + cmd = (b & 1) ? GC_PSX_COMMAND : 0; + parport_write_data(gc->pd->port, cmd | GC_PSX_CLOCK | GC_PSX_POWER); + udelay(GC_PSX_DELAY); + b >>= 1; + } + return ret; +} + +/* + * gc_psx_read_packet() reads a whole psx packet and returns + * device identifier code. + */ + +static int gc_psx_read_packet(struct gc *gc, int length, unsigned char *data) +{ + int i, ret; + unsigned long flags; + + __save_flags(flags); + __cli(); + + parport_write_data(gc->pd->port, GC_PSX_POWER); + + parport_write_data(gc->pd->port, GC_PSX_CLOCK | GC_PSX_SELECT | GC_PSX_POWER); /* Select pad */ + udelay(GC_PSX_DELAY * 2); + gc_psx_command(gc, 0x01); /* Access pad */ + ret = gc_psx_command(gc, 0x42); /* Get device id */ + if (gc_psx_command(gc, 0) == 'Z') /* okay? */ + for (i = 0; i < length; i++) + data[i] = gc_psx_command(gc, 0); + else ret = -1; + + parport_write_data(gc->pd->port, GC_PSX_SELECT | GC_PSX_CLOCK | GC_PSX_POWER); + __restore_flags(flags); + + return ret; +} + +/* + * gc_timer() reads and analyzes console pads data. + */ + +#define GC_MAX_LENGTH GC_N64_LENGTH + +static void gc_timer(unsigned long private) +{ + struct gc *gc = (void *) private; + struct input_dev *dev = gc->dev; + unsigned char data[GC_MAX_LENGTH]; + int i, j, s; + +/* + * N64 pads - must be read first, any read confuses them for 200 us + */ + + if (gc->pads[GC_N64]) { + + gc_n64_read_packet(gc, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & gc->pads[GC_N64] & ~(data[8] | data[9])) { + + signed char axes[2]; + axes[0] = axes[1] = 0; + + for (j = 0; j < 8; j++) { + if (data[23 - j] & s) axes[0] |= 1 << j; + if (data[31 - j] & s) axes[1] |= 1 << j; + } + + input_report_abs(dev + i, ABS_X, axes[0]); + input_report_abs(dev + i, ABS_Y, -axes[1]); + + input_report_abs(dev + i, ABS_HAT0X, !!(s & data[7]) - !!(s & data[6])); + input_report_abs(dev + i, ABS_HAT0Y, !!(s & data[5]) - !!(s & data[4])); + + for (j = 0; j < 10; j++) + input_report_key(dev + i, gc_n64_btn[j], s & data[gc_n64_bytes[j]]); + } + } + } + +/* + * NES and SNES pads + */ + + if (gc->pads[GC_NES] || gc->pads[GC_SNES]) { + + gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_NES] | gc->pads[GC_SNES])) { + input_report_abs(dev + i, ABS_X, !!(s & data[7]) - !!(s & data[6])); + input_report_abs(dev + i, ABS_Y, !!(s & data[5]) - !!(s & data[4])); + } + + if (s & gc->pads[GC_NES]) + for (j = 0; j < 4; j++) + input_report_key(dev + i, gc_snes_btn[j], s & data[gc_nes_bytes[j]]); + + if (s & gc->pads[GC_SNES]) + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_snes_btn[j], s & data[gc_snes_bytes[j]]); + } + } + +/* + * Multi and Multi2 joysticks + */ + + if (gc->pads[GC_MULTI] || gc->pads[GC_MULTI2]) { + + gc_multi_read_packet(gc, gc->pads[GC_MULTI2] ? GC_MULTI2_LENGTH : GC_MULTI_LENGTH, data); + + for (i = 0; i < 5; i++) { + + s = gc_status_bit[i]; + + if (s & (gc->pads[GC_MULTI] | gc->pads[GC_MULTI2])) { + input_report_abs(dev + i, ABS_X, !!(s & data[3]) - !!(s & data[2])); + input_report_abs(dev + i, ABS_Y, !!(s & data[1]) - !!(s & data[0])); + input_report_key(dev + i, BTN_TRIGGER, s & data[4]); + } + + if (s & gc->pads[GC_MULTI2]) + input_report_key(dev + i, BTN_THUMB, s & data[5]); + } + } + +/* + * PSX controllers + */ + + if (gc->pads[GC_PSX]) { + + for (i = 0; i < 5; i++) + if (gc->pads[GC_PSX] & gc_status_bit[i]) + break; + + switch (gc_psx_read_packet(gc, 6, data)) { + + case GC_PSX_ANALOGG: + + for (j = 0; j < 4; j++) + input_report_abs(dev + i, gc_psx_abs[j], data[j + 2]); + + input_report_abs(dev + i, ABS_HAT0X, !!(data[0]&0x20) - !!(data[0]&0x80)); + input_report_abs(dev + i, ABS_HAT0Y, !!(data[0]&0x40) - !!(data[0]&0x10)); + + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << i)); + + input_report_key(dev + i, BTN_START, ~data[0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + + break; + + case GC_PSX_ANALOGR: + + input_report_key(dev + i, BTN_THUMB, ~data[0] & 0x04); + input_report_key(dev + i, BTN_THUMB2, ~data[0] & 0x02); + + case GC_PSX_NORMAL: + case GC_PSX_NEGCON: + + input_report_abs(dev + i, ABS_X, 128 + !!(data[0] & 0x20) * 127 - !!(data[0] & 0x80) * 128); + input_report_abs(dev + i, ABS_Y, 128 + !!(data[0] & 0x40) * 127 - !!(data[0] & 0x10) * 128); + + for (j = 0; j < 8; j++) + input_report_key(dev + i, gc_psx_btn[j], ~data[1] & (1 << i)); + + input_report_key(dev + i, BTN_START, ~data[0] & 0x08); + input_report_key(dev + i, BTN_SELECT, ~data[0] & 0x01); + + break; + } + } + + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); +} + +static int gc_open(struct input_dev *dev) +{ + struct gc *gc = dev->private; + if (!gc->used++) { + parport_claim(gc->pd); + parport_write_control(gc->pd->port, 0x04); + mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); + } + return 0; +} + +static void gc_close(struct input_dev *dev) +{ + struct gc *gc = dev->private; + if (!--gc->used) { + del_timer(&gc->timer); + parport_write_control(gc->pd->port, 0x00); + parport_release(gc->pd); + } +} + + + +static struct gc __init *gc_probe(int *config) +{ + struct gc *gc; + struct parport *pp; + int i, j, psx, pbtn; + unsigned char data[2]; + + if (config[0] < 0) + return NULL; + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "gamecon.c: no such parport\n"); + return NULL; + } + + if (!(gc = kmalloc(sizeof(struct gc), GFP_KERNEL))) + return NULL; + memset(gc, 0, sizeof(struct gc)); + + gc->pd = parport_register_device(pp, "gamecon", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!gc->pd) { + printk(KERN_ERR "gamecon.c: parport busy already - lp.o loaded?\n"); + kfree(gc); + return NULL; + } + + parport_claim(gc->pd); + + init_timer(&gc->timer); + gc->timer.data = (long) gc; + gc->timer.function = gc_timer; + + for (i = 0; i < 5; i++) { + + if (!config[i + 1]) + continue; + + if (config[i + 1] < 1 || config[i + 1] > GC_MAX) { + printk(KERN_WARNING "gamecon.c: Pad type %d unknown\n", config[i + 1]); + continue; + } + + gc->dev[i].private = gc; + gc->dev[i].open = gc_open; + gc->dev[i].close = gc_close; + + gc->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; j < 2; j++) { + set_bit(ABS_X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_X + j] = -1; + gc->dev[i].absmax[ABS_X + j] = 1; + } + + gc->pads[0] |= gc_status_bit[i]; + gc->pads[config[i + 1]] |= gc_status_bit[i]; + + switch(config[i + 1]) { + + case GC_N64: + for (j = 0; j < 10; j++) + set_bit(gc_n64_btn[j], gc->dev[j].keybit); + + for (j = 0; j < 2; j++) { + set_bit(ABS_X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_X + j] = -127; + gc->dev[i].absmax[ABS_X + j] = 126; + gc->dev[i].absflat[ABS_X + j] = 2; + set_bit(ABS_HAT0X + j, gc->dev[i].absbit); + gc->dev[i].absmin[ABS_HAT0X + j] = -1; + gc->dev[i].absmax[ABS_HAT0X + j] = 1; + } + + break; + + case GC_SNES: + for (j = 0; j < 8; j++) + set_bit(gc_snes_btn[j], gc->dev[j].keybit); + break; + + case GC_NES: + for (j = 0; j < 4; j++) + set_bit(gc_snes_btn[j], gc->dev[j].keybit); + break; + + case GC_MULTI2: + set_bit(BTN_THUMB, gc->dev[i].keybit); + + case GC_MULTI: + set_bit(BTN_TRIGGER, gc->dev[i].keybit); + break; + + case GC_PSX: + + psx = gc_psx_read_packet(gc, 2, data); + + switch(psx) { + case GC_PSX_NEGCON: + config[i + 1] += 1; + case GC_PSX_NORMAL: + pbtn = 10; + break; + + case GC_PSX_ANALOGG: + case GC_PSX_ANALOGR: + config[i + 1] += 2; + pbtn = 12; + for (j = 0; j < 6; j++) { + psx = gc_psx_abs[j]; + set_bit(psx, gc->dev[i].absbit); + gc->dev[i].absmin[psx] = 4; + gc->dev[i].absmax[psx] = 252; + gc->dev[i].absflat[psx] = 2; + } + break; + + case -1: + gc->pads[GC_PSX] &= ~gc_status_bit[i]; + pbtn = 0; + printk(KERN_ERR "gamecon.c: No PSX controller found.\n"); + break; + + default: + gc->pads[GC_PSX] &= ~gc_status_bit[i]; + pbtn = 0; + printk(KERN_WARNING "gamecon.c: Unsupported PSX controller %#x," + " please report to <vojtech@suse.cz>.\n", psx); + } + + for (j = 0; j < pbtn; j++) + set_bit(gc_psx_btn[j], gc->dev[i].keybit); + + break; + } + + gc->dev[i].name = gc_names[config[i + 1]]; + gc->dev[i].idbus = BUS_PARPORT; + gc->dev[i].idvendor = 0x0001; + gc->dev[i].idproduct = config[i + 1]; + gc->dev[i].idversion = 0x0100; + } + + parport_release(gc->pd); + + if (!gc->pads[0]) { + parport_unregister_device(gc->pd); + kfree(gc); + return NULL; + } + + for (i = 0; i < 5; i++) + if (gc->pads[0] & gc_status_bit[i]) { + input_register_device(gc->dev + i); + printk(KERN_INFO "input%d: %s on %s\n", gc->dev[i].number, gc->dev[i].name, gc->pd->port->name); + } + + return gc; +} + +#ifndef MODULE +int __init gc_setup(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc[i] = ints[i + 1]; + return 1; +} +int __init gc_setup_2(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc_2[i] = ints[i + 1]; + return 1; +} +int __init gc_setup_3(char *str) +{ + int i, ints[7]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 6; i++) gc_3[i] = ints[i + 1]; + return 1; +} +__setup("gc=", gc_setup); +__setup("gc_2=", gc_setup_2); +__setup("gc_3=", gc_setup_3); +#endif + +int __init gc_init(void) +{ + gc_base[0] = gc_probe(gc); + gc_base[1] = gc_probe(gc_2); + gc_base[2] = gc_probe(gc_3); + + if (gc_base[0] || gc_base[1] || gc_base[2]) + return 0; + + return -ENODEV; +} + +void __exit gc_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 5; j++) + if (gc_base[i]->pads[0] & gc_status_bit[j]) + input_unregister_device(gc_base[i]->dev + j); + parport_unregister_device(gc_base[i]->pd); + } +} + +module_init(gc_init); +module_exit(gc_exit); diff --git a/drivers/char/joystick/gameport.c b/drivers/char/joystick/gameport.c new file mode 100644 index 000000000..5a5e1219b --- /dev/null +++ b/drivers/char/joystick/gameport.c @@ -0,0 +1,198 @@ +/* + * $Id: gameport.c,v 1.5 2000/05/29 10:54:53 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Generic gameport layer + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> +#include <linux/isapnp.h> +#include <linux/stddef.h> +#include <linux/delay.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +EXPORT_SYMBOL(gameport_register_port); +EXPORT_SYMBOL(gameport_unregister_port); +EXPORT_SYMBOL(gameport_register_device); +EXPORT_SYMBOL(gameport_unregister_device); +EXPORT_SYMBOL(gameport_open); +EXPORT_SYMBOL(gameport_close); +EXPORT_SYMBOL(gameport_rescan); +EXPORT_SYMBOL(gameport_cooked_read); + +static struct gameport *gameport_list = NULL; +static struct gameport_dev *gameport_dev = NULL; +static int gameport_number = 0; + +/* + * gameport_measure_speed() measures the gameport i/o speed. + */ + +static int gameport_measure_speed(struct gameport *gameport) +{ +#ifdef __i386__ + +#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) +#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) + + unsigned int i, t, t1, t2, t3, tx; + unsigned long flags; + + if (gameport_open(gameport, NULL, GAMEPORT_MODE_RAW)) + return 0; + + tx = 1 << 30; + + for(i = 0; i < 50; i++) { + save_flags(flags); /* Yes, all CPUs */ + cli(); + GET_TIME(t1); + for(t = 0; t < 50; t++) gameport_read(gameport); + GET_TIME(t2); + GET_TIME(t3); + restore_flags(flags); + udelay(i * 10); + if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; + } + + return 59659 / (tx < 1 ? 1 : tx); + +#else + + unsigned int j, t = 0; + + j = jiffies; while (j == jiffies); + j = jiffies; while (j == jiffies) { t++; gameport_read(gameport); } + + return t * HZ / 1000; + +#endif + + gameport_close(gameport); +} + +static void gameport_find_dev(struct gameport *gameport) +{ + struct gameport_dev *dev = gameport_dev; + + while (dev && !gameport->dev) { + if (dev->connect) + dev->connect(gameport, dev); + dev = dev->next; + } +} + +void gameport_rescan(struct gameport *gameport) +{ + gameport_close(gameport); + gameport_find_dev(gameport); +} + +void gameport_register_port(struct gameport *gameport) +{ + gameport->number = gameport_number++; + gameport->next = gameport_list; + gameport_list = gameport; + + gameport->speed = gameport_measure_speed(gameport); + + gameport_find_dev(gameport); +} + +void gameport_unregister_port(struct gameport *gameport) +{ + struct gameport **gameportptr = &gameport_list; + + while (*gameportptr && (*gameportptr != gameport)) gameportptr = &((*gameportptr)->next); + *gameportptr = (*gameportptr)->next; + + if (gameport->dev && gameport->dev->disconnect) + gameport->dev->disconnect(gameport); + + gameport_number--; +} + +void gameport_register_device(struct gameport_dev *dev) +{ + struct gameport *gameport = gameport_list; + + dev->next = gameport_dev; + gameport_dev = dev; + + while (gameport) { + if (!gameport->dev && dev->connect) + dev->connect(gameport, dev); + gameport = gameport->next; + } +} + +void gameport_unregister_device(struct gameport_dev *dev) +{ + struct gameport_dev **devptr = &gameport_dev; + struct gameport *gameport = gameport_list; + + while (*devptr && (*devptr != dev)) devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + while (gameport) { + if (gameport->dev == dev && dev->disconnect) + dev->disconnect(gameport); + gameport_find_dev(gameport); + gameport = gameport->next; + } +} + +int gameport_open(struct gameport *gameport, struct gameport_dev *dev, int mode) +{ + if (gameport->open) { + if (gameport->open(gameport, mode)) + return -1; + } else { + if (mode != GAMEPORT_MODE_RAW) + return -1; + } + + if (gameport->dev) + return -1; + + gameport->dev = dev; + + return 0; +} + +void gameport_close(struct gameport *gameport) +{ + gameport->dev = NULL; + if (gameport->close) gameport->close(gameport); +} diff --git a/drivers/char/joystick/gf2k.c b/drivers/char/joystick/gf2k.c new file mode 100644 index 000000000..cad8be16b --- /dev/null +++ b/drivers/char/joystick/gf2k.c @@ -0,0 +1,359 @@ +/* + * $Id: gf2k.c,v 1.12 2000/06/04 14:53:44 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Genius Flight 2000 joystick driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +#define GF2K_START 400 /* The time we wait for the first bit [400 us] */ +#define GF2K_STROBE 40 /* The time we wait for the first bit [40 us] */ +#define GF2K_TIMEOUT 4 /* Wait for everything to settle [4 ms] */ +#define GF2K_LENGTH 80 /* Max number of triplets in a packet */ +#define GF2K_REFRESH HZ/50 /* Time between joystick polls [20 ms] */ + +/* + * Genius joystick ids ... + */ + +#define GF2K_ID_G09 1 +#define GF2K_ID_F30D 2 +#define GF2K_ID_F30 3 +#define GF2K_ID_F31D 4 +#define GF2K_ID_F305 5 +#define GF2K_ID_F23P 6 +#define GF2K_ID_F31 7 +#define GF2K_ID_MAX 7 + +static char gf2k_length[] = { 40, 40, 40, 40, 40, 40, 40, 40 }; +static char gf2k_hat_to_axis[][2] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *gf2k_names[] = {"", "Genius G-09D", "Genius F-30D", "Genius F-30", "Genius MaxFighter F-31D", + "Genius F-30-5", "Genius Flight2000 F-23", "Genius F-31"}; +static unsigned char gf2k_hats[] = { 0, 2, 0, 0, 2, 0, 2, 0 }; +static unsigned char gf2k_axes[] = { 0, 2, 0, 0, 4, 0, 4, 0 }; +static unsigned char gf2k_joys[] = { 0, 0, 0, 0,10, 0, 8, 0 }; +static unsigned char gf2k_pads[] = { 0, 6, 0, 0, 0, 0, 0, 0 }; +static unsigned char gf2k_lens[] = { 0,18, 0, 0,18, 0,18, 0 }; + +static unsigned char gf2k_abs[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_RUDDER, ABS_GAS, ABS_BRAKE }; +static short gf2k_btn_joy[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4 }; +static short gf2k_btn_pad[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_START, BTN_SELECT }; + + +static short gf2k_seq_reset[] = { 240, 340, 0 }; +static short gf2k_seq_digital[] = { 590, 320, 860, 0 }; + +struct gf2k { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev; + int reads; + int bads; + int used; + unsigned char id; + unsigned char length; +}; + +/* + * gf2k_read_packet() reads a Genius Flight2000 packet. + */ + +static int gf2k_read_packet(struct gameport *gameport, int length, char *data) +{ + unsigned char u, v; + int i; + unsigned int t, p; + unsigned long flags; + + t = gameport_time(gameport, GF2K_START); + p = gameport_time(gameport, GF2K_STROBE); + + i = 0; + + __save_flags(flags); + __cli(); + + gameport_trigger(gameport); + v = gameport_read(gameport);; + + while (t > 0 && i < length) { + t--; u = v; + v = gameport_read(gameport); + if (v & ~u & 0x10) { + data[i++] = v >> 5; + t = p; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * gf2k_trigger_seq() initializes a Genius Flight2000 joystick + * into digital mode. + */ + +static void gf2k_trigger_seq(struct gameport *gameport, short *seq) +{ + + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + gameport_trigger(gameport); + t = gameport_time(gameport, GF2K_TIMEOUT * 1000); + while ((gameport_read(gameport) & 1) && t) t--; + udelay(seq[i]); + } while (seq[++i]); + + gameport_trigger(gameport); + + __restore_flags(flags); +} + +/* + * js_sw_get_bits() composes bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(p,n,s) gf2k_get_bits(data, p, n, s) + +static int gf2k_get_bits(unsigned char *buf, int pos, int num, int shift) +{ + __u64 data = 0; + int i; + + for (i = 0; i < num / 3 + 2; i++) + data |= buf[pos / 3 + i] << (i * 3); + data >>= pos % 3; + data &= (1 << num) - 1; + data <<= shift; + + return data; +} + +static void gf2k_read(struct gf2k *gf2k, unsigned char *data) +{ + struct input_dev *dev = &gf2k->dev; + int i, t; + + for (i = 0; i < 4 && i < gf2k_axes[gf2k->id]; i++) + input_report_abs(dev, gf2k_abs[i], GB(i<<3,8,0) | GB(i+46,1,8) | GB(i+50,1,9)); + + for (i = 0; i < 2 && i < gf2k_axes[gf2k->id] - 4; i++) + input_report_abs(dev, gf2k_abs[i], GB(i*9+60,8,0) | GB(i+54,1,9)); + + t = GB(40,4,0); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) + input_report_abs(dev, ABS_HAT0X + i, gf2k_hat_to_axis[t][i]); + + t = GB(44,2,0) | GB(32,8,2) | GB(78,2,10); + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_joy[i], (t >> i) & 1); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + input_report_key(dev, gf2k_btn_pad[i], (t >> i) & 1); +} + +/* + * gf2k_timer() reads and analyzes Genius joystick data. + */ + +static void gf2k_timer(unsigned long private) +{ + struct gf2k *gf2k = (void *) private; + unsigned char data[GF2K_LENGTH]; + + gf2k->reads++; + + if (gf2k_read_packet(gf2k->gameport, gf2k_length[gf2k->id], data) < gf2k_length[gf2k->id]) { + gf2k->bads++; + } else gf2k_read(gf2k, data); + + mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH); +} + +static int gf2k_open(struct input_dev *dev) +{ + struct gf2k *gf2k = dev->private; + if (!gf2k->used++) + mod_timer(&gf2k->timer, jiffies + GF2K_REFRESH); + return 0; +} + +static void gf2k_close(struct input_dev *dev) +{ + struct gf2k *gf2k = dev->private; + if (!--gf2k->used) + del_timer(&gf2k->timer); +} + +/* + * gf2k_connect() probes for Genius id joysticks. + */ + +static void gf2k_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct gf2k *gf2k; + unsigned char data[GF2K_LENGTH]; + int i; + + if (!(gf2k = kmalloc(sizeof(struct gf2k), GFP_KERNEL))) + return; + memset(gf2k, 0, sizeof(struct gf2k)); + + gameport->private = gf2k; + + gf2k->gameport = gameport; + init_timer(&gf2k->timer); + gf2k->timer.data = (long) gf2k; + gf2k->timer.function = gf2k_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + gf2k_trigger_seq(gameport, gf2k_seq_reset); + + wait_ms(GF2K_TIMEOUT); + + gf2k_trigger_seq(gameport, gf2k_seq_digital); + + wait_ms(GF2K_TIMEOUT); + + if (gf2k_read_packet(gameport, GF2K_LENGTH, data) < 12) + goto fail2; + + if (!(gf2k->id = GB(7,2,0) | GB(3,3,2) | GB(0,3,5))) + goto fail2; + +#ifdef RESET_WORKS + if ((gf2k->id != (GB(19,2,0) | GB(15,3,2) | GB(12,3,5))) || + (gf2k->id != (GB(31,2,0) | GB(27,3,2) | GB(24,3,5)))) + goto fail2; +#else + gf2k->id = 6; +#endif + + if (gf2k->id > GF2K_ID_MAX || !gf2k_axes[gf2k->id]) { + printk(KERN_WARNING "gf2k.c: Not yet supported joystick on gameport%d. [id: %d type:%s]\n", + gameport->number, gf2k->id, gf2k->id > GF2K_ID_MAX ? "Unknown" : gf2k_names[gf2k->id]); + goto fail2; + } + + gf2k->length = gf2k_lens[gf2k->id]; + + gf2k->dev.private = gf2k; + gf2k->dev.open = gf2k_open; + gf2k->dev.close = gf2k_close; + gf2k->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + gf2k->dev.name = gf2k_names[gf2k->id]; + gf2k->dev.idbus = BUS_GAMEPORT; + gf2k->dev.idvendor = GAMEPORT_ID_VENDOR_GENIUS; + gf2k->dev.idproduct = gf2k->id; + gf2k->dev.idversion = 0x0100; + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) + set_bit(gf2k_abs[i], gf2k->dev.absbit); + + for (i = 0; i < gf2k_hats[gf2k->id]; i++) { + set_bit(ABS_HAT0X + i, gf2k->dev.absbit); + gf2k->dev.absmin[ABS_HAT0X + i] = -1; + gf2k->dev.absmax[ABS_HAT0X + i] = 1; + } + + for (i = 0; i < gf2k_joys[gf2k->id]; i++) + set_bit(gf2k_btn_joy[i], gf2k->dev.keybit); + + for (i = 0; i < gf2k_pads[gf2k->id]; i++) + set_bit(gf2k_btn_pad[i], gf2k->dev.keybit); + + gf2k_read_packet(gameport, gf2k->length, data); + gf2k_read(gf2k, data); + + for (i = 0; i < gf2k_axes[gf2k->id]; i++) { + gf2k->dev.absmax[gf2k_abs[i]] = (i < 2) ? gf2k->dev.abs[gf2k_abs[i]] * 2 - 32 : + gf2k->dev.abs[gf2k_abs[0]] + gf2k->dev.abs[gf2k_abs[1]] - 32; + gf2k->dev.absmin[gf2k_abs[i]] = 32; + gf2k->dev.absfuzz[gf2k_abs[i]] = 8; + gf2k->dev.absflat[gf2k_abs[i]] = (i < 2) ? 24 : 0; + } + + input_register_device(&gf2k->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + gf2k->dev.number, gf2k_names[gf2k->id], gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(gf2k); +} + +static void gf2k_disconnect(struct gameport *gameport) +{ + struct gf2k *gf2k = gameport->private; + input_unregister_device(&gf2k->dev); + gameport_close(gameport); + kfree(gf2k); +} + +static struct gameport_dev gf2k_dev = { + connect: gf2k_connect, + disconnect: gf2k_disconnect, +}; + +int __init gf2k_init(void) +{ + gameport_register_device(&gf2k_dev); + return 0; +} + +void __exit gf2k_exit(void) +{ + gameport_unregister_device(&gf2k_dev); +} + +module_init(gf2k_init); +module_exit(gf2k_exit); diff --git a/drivers/char/joystick/grip.c b/drivers/char/joystick/grip.c new file mode 100644 index 000000000..4cedd7892 --- /dev/null +++ b/drivers/char/joystick/grip.c @@ -0,0 +1,423 @@ +/* + * $Id: grip.c,v 1.14 2000/06/06 21:13:36 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Gravis/Kensington GrIP protocol joystick and gamepad driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define GRIP_MODE_GPP 1 +#define GRIP_MODE_BD 2 +#define GRIP_MODE_XT 3 +#define GRIP_MODE_DC 4 + +#define GRIP_LENGTH_GPP 24 +#define GRIP_STROBE_GPP 200 /* 200 us */ +#define GRIP_LENGTH_XT 4 +#define GRIP_STROBE_XT 64 /* 64 us */ +#define GRIP_MAX_CHUNKS_XT 10 +#define GRIP_MAX_BITS_XT 30 + +#define GRIP_REFRESH_TIME HZ/50 /* 20 ms */ + +struct grip { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + unsigned char mode[2]; + int used; + int reads; + int bads; +}; + +static int grip_btn_gpp[] = { BTN_START, BTN_SELECT, BTN_TR2, BTN_Y, 0, BTN_TL2, BTN_A, BTN_B, BTN_X, 0, BTN_TL, BTN_TR, -1 }; +static int grip_btn_bd[] = { BTN_THUMB, BTN_THUMB2, BTN_TRIGGER, BTN_TOP, BTN_BASE, -1 }; +static int grip_btn_xt[] = { BTN_TRIGGER, BTN_THUMB, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_SELECT, BTN_START, BTN_MODE, -1 }; +static int grip_btn_dc[] = { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_BASE5, -1 }; + +static int grip_abs_gpp[] = { ABS_X, ABS_Y, -1 }; +static int grip_abs_bd[] = { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; +static int grip_abs_xt[] = { ABS_X, ABS_Y, ABS_BRAKE, ABS_GAS, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, -1 }; +static int grip_abs_dc[] = { ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y, -1 }; + +static char *grip_name[] = { NULL, "Gravis GamePad Pro", "Gravis Blackhawk Digital", + "Gravis Xterminator Digital", "Gravis Xterminator DualControl" }; +static int *grip_abs[] = { 0, grip_abs_gpp, grip_abs_bd, grip_abs_xt, grip_abs_dc }; +static int *grip_btn[] = { 0, grip_btn_gpp, grip_btn_bd, grip_btn_xt, grip_btn_dc }; +static char grip_anx[] = { 0, 0, 3, 5, 5 }; +static char grip_cen[] = { 0, 0, 2, 2, 4 }; + +/* + * grip_gpp_read_packet() reads a Gravis GamePad Pro packet. + */ + +static int grip_gpp_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t; + int i; + + int strobe = gameport_time(gameport, GRIP_STROBE_GPP); + + data[0] = 0; + t = strobe; + i = 0; + + __save_flags(flags); + __cli(); + + v = gameport_read(gameport) >> shift; + + do { + t--; + u = v; v = (gameport_read(gameport) >> shift) & 3; + if (~v & u & 1) { + data[0] |= (v >> 1) << i++; + t = strobe; + } + } while (i < GRIP_LENGTH_GPP && t > 0); + + __restore_flags(flags); + + if (i < GRIP_LENGTH_GPP) return -1; + + for (i = 0; i < GRIP_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) + data[0] = data[0] >> 1 | (data[0] & 1) << (GRIP_LENGTH_GPP - 1); + + return -(i == GRIP_LENGTH_GPP); +} + +/* + * grip_xt_read_packet() reads a Gravis Xterminator packet. + */ + +static int grip_xt_read_packet(struct gameport *gameport, int shift, unsigned int *data) +{ + unsigned int i, j, buf, crc; + unsigned char u, v, w; + unsigned long flags; + unsigned int t; + char status; + + int strobe = gameport_time(gameport, GRIP_STROBE_XT); + + data[0] = data[1] = data[2] = data[3] = 0; + status = buf = i = j = 0; + t = strobe; + + __save_flags(flags); + __cli(); + + v = w = (gameport_read(gameport) >> shift) & 3; + + do { + t--; + u = (gameport_read(gameport) >> shift) & 3; + + if (u ^ v) { + + if ((u ^ v) & 1) { + buf = (buf << 1) | (u >> 1); + t = strobe; + i++; + } else + + if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { + if (i == 20) { + crc = buf ^ (buf >> 7) ^ (buf >> 14); + if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { + data[buf >> 18] = buf >> 4; + status |= 1 << (buf >> 18); + } + j++; + } + t = strobe; + buf = 0; + i = 0; + } + w = v; + v = u; + } + + } while (status != 0xf && i < GRIP_MAX_BITS_XT && j < GRIP_MAX_CHUNKS_XT && t > 0); + + __restore_flags(flags); + + return -(status != 0xf); +} + +/* + * grip_timer() repeatedly polls the joysticks and generates events. + */ + +static void grip_timer(unsigned long private) +{ + struct grip *grip = (void*) private; + unsigned int data[GRIP_LENGTH_XT]; + struct input_dev *dev; + int i, j; + + for (i = 0; i < 2; i++) { + + dev = grip->dev + i; + grip->reads++; + + switch (grip->mode[i]) { + + case GRIP_MODE_GPP: + + if (grip_gpp_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, ((*data >> 15) & 1) - ((*data >> 16) & 1)); + input_report_abs(dev, ABS_Y, ((*data >> 13) & 1) - ((*data >> 12) & 1)); + + for (j = 0; j < 12; j++) + if (grip_btn_gpp[j]) + input_report_key(dev, grip_btn_gpp[j], (*data >> j) & 1); + + break; + + case GRIP_MODE_BD: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 5; j++) + input_report_key(dev, grip_btn_bd[j], (data[3] >> (j + 4)) & 1); + + break; + + case GRIP_MODE_XT: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, 63 - ((data[0] >> 8) & 0x3f)); + input_report_abs(dev, ABS_BRAKE, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_GAS, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + input_report_abs(dev, ABS_HAT1X, ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1)); + input_report_abs(dev, ABS_HAT1Y, ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1)); + + for (j = 0; j < 11; j++) + input_report_key(dev, grip_btn_xt[j], (data[3] >> (j + 3)) & 1); + break; + + case GRIP_MODE_DC: + + if (grip_xt_read_packet(grip->gameport, (i << 1) + 4, data)) { + grip->bads++; + break; + } + + input_report_abs(dev, ABS_X, (data[0] >> 2) & 0x3f); + input_report_abs(dev, ABS_Y, (data[0] >> 8) & 0x3f); + input_report_abs(dev, ABS_RX, (data[1] >> 2) & 0x3f); + input_report_abs(dev, ABS_RY, (data[1] >> 8) & 0x3f); + input_report_abs(dev, ABS_THROTTLE, (data[2] >> 8) & 0x3f); + + input_report_abs(dev, ABS_HAT0X, ((data[2] >> 1) & 1) - ( data[2] & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1)); + + for (j = 0; j < 9; j++) + input_report_key(dev, grip_btn_dc[j], (data[3] >> (j + 3)) & 1); + break; + + + } + } + + mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME); +} + +static int grip_open(struct input_dev *dev) +{ + struct grip *grip = dev->private; + if (!grip->used++) + mod_timer(&grip->timer, jiffies + GRIP_REFRESH_TIME); + return 0; +} + +static void grip_close(struct input_dev *dev) +{ + struct grip *grip = dev->private; + if (!--grip->used) + del_timer(&grip->timer); +} + +static void grip_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct grip *grip; + unsigned int data[GRIP_LENGTH_XT]; + int i, j, t; + + if (!(grip = kmalloc(sizeof(struct grip), GFP_KERNEL))) + return; + memset(grip, 0, sizeof(struct grip)); + + gameport->private = grip; + + grip->gameport = gameport; + init_timer(&grip->timer); + grip->timer.data = (long) grip; + grip->timer.function = grip_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + for (i = 0; i < 2; i++) { + if (!grip_gpp_read_packet(gameport, (i << 1) + 4, data)) { + grip->mode[i] = GRIP_MODE_GPP; + continue; + } + if (!grip_xt_read_packet(gameport, (i << 1) + 4, data)) { + if (!(data[3] & 7)) { + grip->mode[i] = GRIP_MODE_BD; + continue; + } + if (!(data[2] & 0xf0)) { + grip->mode[i] = GRIP_MODE_XT; + continue; + } + grip->mode[i] = GRIP_MODE_DC; + continue; + } + } + + if (!grip->mode[0] && !grip->mode[1]) + goto fail2; + + for (i = 0; i < 2; i++) + if (grip->mode[i]) { + + grip->dev[i].private = grip; + + grip->dev[i].open = grip_open; + grip->dev[i].close = grip_close; + + grip->dev[i].name = grip_name[grip->mode[i]]; + grip->dev[i].idbus = BUS_GAMEPORT; + grip->dev[i].idvendor = GAMEPORT_ID_VENDOR_GRAVIS; + grip->dev[i].idproduct = grip->mode[i]; + grip->dev[i].idversion = 0x0100; + + grip->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; (t = grip_abs[grip->mode[i]][j]) >= 0; j++) { + + set_bit(t, grip->dev[i].absbit); + + if (j < grip_cen[grip->mode[i]]) { + grip->dev[i].absmin[t] = 14; + grip->dev[i].absmax[t] = 52; + grip->dev[i].absfuzz[t] = 1; + grip->dev[i].absflat[t] = 2; + continue; + } + + if (j < grip_anx[grip->mode[i]]) { + grip->dev[i].absmin[t] = 3; + grip->dev[i].absmax[t] = 57; + grip->dev[i].absfuzz[t] = 1; + continue; + } + + grip->dev[i].absmin[t] = -1; + grip->dev[i].absmax[t] = 1; + } + + for (j = 0; (t = grip_btn[grip->mode[i]][j]) >= 0; j++) + if (t > 0) + set_bit(t, grip->dev[i].keybit); + + input_register_device(grip->dev + i); + + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + grip->dev[i].number, grip_name[grip->mode[i]], gameport->number, i); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(grip); +} + +static void grip_disconnect(struct gameport *gameport) +{ + int i; + + struct grip *grip = gameport->private; + for (i = 0; i < 2; i++) + if (grip->mode[i]) + input_unregister_device(grip->dev + i); + gameport_close(gameport); + kfree(grip); +} + +static struct gameport_dev grip_dev = { + connect: grip_connect, + disconnect: grip_disconnect, +}; + +int __init grip_init(void) +{ + gameport_register_device(&grip_dev); + return 0; +} + +void __exit grip_exit(void) +{ + gameport_unregister_device(&grip_dev); +} + +module_init(grip_init); +module_exit(grip_exit); diff --git a/drivers/char/joystick/interact.c b/drivers/char/joystick/interact.c new file mode 100644 index 000000000..7104e5d49 --- /dev/null +++ b/drivers/char/joystick/interact.c @@ -0,0 +1,306 @@ +/* + * $Id: interact.c,v 1.8 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * Based on the work of: + * Toby Deshane + * + * Sponsored by SuSE + */ + +/* + * InterAct digital gamepad/joystick driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define INTERACT_MAX_START 400 /* 400 us */ +#define INTERACT_MAX_STROBE 40 /* 40 us */ +#define INTERACT_MAX_LENGTH 32 /* 32 bits */ +#define INTERACT_REFRESH_TIME HZ/50 /* 20 ms */ + +#define INTERACT_TYPE_HHFX 0 /* HammerHead/FX */ +#define INTERACT_TYPE_PP8D 1 /* ProPad 8 */ + +struct interact { + struct gameport *gameport; + struct input_dev dev; + struct timer_list timer; + int used; + int bads; + int reads; + unsigned char type; + unsigned char length; +}; + +static short interact_abs_hhfx[] = + { ABS_RX, ABS_RY, ABS_X, ABS_Y, ABS_HAT0X, ABS_HAT0Y, -1 }; +static short interact_abs_pp8d[] = + { ABS_X, ABS_Y, -1 }; + +static short interact_btn_hhfx[] = + { BTN_TR, BTN_X, BTN_Y, BTN_Z, BTN_A, BTN_B, BTN_C, BTN_TL, BTN_TL2, BTN_TR2, BTN_MODE, BTN_SELECT, -1 }; +static short interact_btn_pp8d[] = + { BTN_C, BTN_TL, BTN_TR, BTN_A, BTN_B, BTN_Y, BTN_Z, BTN_X, -1 }; + +struct interact_type { + int id; + short *abs; + short *btn; + char *name; + unsigned char length; + unsigned char b8; +}; + +static struct interact_type interact_type[] = { + { 0x6202, interact_abs_hhfx, interact_btn_hhfx, "InterAct HammerHead/FX", 32, 4 }, + { 0x53f8, interact_abs_pp8d, interact_btn_pp8d, "InterAct ProPad 8 Digital", 16, 0 }, + { 0 }}; + +/* + * interact_read_packet() reads and InterAct joystick data. + */ + +static int interact_read_packet(struct gameport *gameport, int length, u32 *data) +{ + unsigned long flags; + unsigned char u, v; + unsigned int t, s; + int i; + + i = 0; + data[0] = data[1] = data[2] = 0; + t = gameport_time(gameport, INTERACT_MAX_START); + s = gameport_time(gameport, INTERACT_MAX_STROBE); + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + v = gameport_read(gameport); + + while (t > 0 && i < length) { + t--; + u = v; v = gameport_read(gameport); + if (v & ~u & 0x40) { + data[0] = (data[0] << 1) | ((v >> 4) & 1); + data[1] = (data[1] << 1) | ((v >> 5) & 1); + data[2] = (data[2] << 1) | ((v >> 7) & 1); + i++; + t = s; + } + } + + __restore_flags(flags); + + return i; +} + +/* + * interact_timer() reads and analyzes InterAct joystick data. + */ + +static void interact_timer(unsigned long private) +{ + struct interact *interact = (struct interact *) private; + struct input_dev *dev = &interact->dev; + u32 data[3]; + int i; + + interact->reads++; + + if (interact_read_packet(interact->gameport, interact->length, data) < interact->length) { + interact->bads++; + } else + + for (i = 0; i < 3; i++) + data[i] <<= INTERACT_MAX_LENGTH - interact->length; + + switch (interact->type) { + + case INTERACT_TYPE_HHFX: + + for (i = 0; i < 4; i++) + input_report_abs(dev, interact_abs_hhfx[i], (data[i & 1] >> ((i >> 1) << 3)) & 0xff); + + for (i = 0; i < 2; i++) + input_report_abs(dev, ABS_HAT0Y - i, + ((data[1] >> ((i << 1) + 17)) & 1) - ((data[1] >> ((i << 1) + 16)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_hhfx[i], (data[0] >> (i + 16)) & 1); + + for (i = 0; i < 4; i++) + input_report_key(dev, interact_btn_hhfx[i + 8], (data[1] >> (i + 20)) & 1); + + break; + + case INTERACT_TYPE_PP8D: + + for (i = 0; i < 2; i++) + input_report_abs(dev, interact_abs_pp8d[i], + ((data[0] >> ((i << 1) + 20)) & 1) - ((data[0] >> ((i << 1) + 21)) & 1)); + + for (i = 0; i < 8; i++) + input_report_key(dev, interact_btn_pp8d[i], (data[1] >> (i + 16)) & 1); + + break; + } + + mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME); + +} + +/* + * interact_open() is a callback from the input open routine. + */ + +static int interact_open(struct input_dev *dev) +{ + struct interact *interact = dev->private; + if (!interact->used++) + mod_timer(&interact->timer, jiffies + INTERACT_REFRESH_TIME); + return 0; +} + +/* + * interact_close() is a callback from the input close routine. + */ + +static void interact_close(struct input_dev *dev) +{ + struct interact *interact = dev->private; + if (!--interact->used) + del_timer(&interact->timer); +} + +/* + * interact_connect() probes for InterAct joysticks. + */ + +static void interact_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct interact *interact; + __u32 data[3]; + int i, t; + + if (!(interact = kmalloc(sizeof(struct interact), GFP_KERNEL))) + return; + memset(interact, 0, sizeof(struct interact)); + + gameport->private = interact; + + interact->gameport = gameport; + init_timer(&interact->timer); + interact->timer.data = (long) interact; + interact->timer.function = interact_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = interact_read_packet(gameport, INTERACT_MAX_LENGTH * 2, data); + + if (i != 32 || (data[0] >> 24) != 0x0c || (data[1] >> 24) != 0x02) { + goto fail2; + } + + for (i = 0; interact_type[i].length; i++) + if (interact_type[i].id == (data[2] >> 16)) + break; + + if (!interact_type[i].length) { + printk(KERN_WARNING "interact.c: Unknown joystick on gameport%d. [len %d d0 %08x d1 %08x i2 %08x]\n", + gameport->number, i, data[0], data[1], data[2]); + goto fail2; + } + + interact->type = i; + interact->length = interact_type[i].length; + + interact->dev.private = interact; + interact->dev.open = interact_open; + interact->dev.close = interact_close; + + interact->dev.name = interact_type[i].name; + interact->dev.idbus = BUS_GAMEPORT; + interact->dev.idvendor = GAMEPORT_ID_VENDOR_INTERACT; + interact->dev.idproduct = interact_type[i].id; + interact->dev.idversion = 0x0100; + + interact->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; (t = interact_type[interact->type].abs[i]) >= 0; i++) { + set_bit(t, interact->dev.absbit); + if (i < interact_type[interact->type].b8) { + interact->dev.absmin[t] = 0; + interact->dev.absmax[t] = 255; + } else { + interact->dev.absmin[t] = -1; + interact->dev.absmax[t] = 1; + } + } + + for (i = 0; (t = interact_type[interact->type].btn[i]) >= 0; i++) + set_bit(t, interact->dev.keybit); + + input_register_device(&interact->dev); + printk(KERN_INFO "input%d: %s on gameport%d.0\n", + interact->dev.number, interact_type[interact->type].name, gameport->number); + + return; +fail2: gameport_close(gameport); +fail1: kfree(interact); +} + +static void interact_disconnect(struct gameport *gameport) +{ + struct interact *interact = gameport->private; + input_unregister_device(&interact->dev); + gameport_close(gameport); + kfree(interact); +} + +static struct gameport_dev interact_dev = { + connect: interact_connect, + disconnect: interact_disconnect, +}; + +int __init interact_init(void) +{ + gameport_register_device(&interact_dev); + return 0; +} + +void __exit interact_exit(void) +{ + gameport_unregister_device(&interact_dev); +} + +module_init(interact_init); +module_exit(interact_exit); diff --git a/drivers/char/joystick/joy-amiga.c b/drivers/char/joystick/joy-amiga.c deleted file mode 100644 index 8c0ba6923..000000000 --- a/drivers/char/joystick/joy-amiga.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * joy-amiga.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * microswitch based joystick connected to Amiga joystick port. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/system.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <asm/amigahw.h> -#include <linux/init.h> - -static struct js_port* js_am_port __initdata = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_am, "1-2i"); - -static int __initdata js_am[] = { 0, 0 }; - -/* - * js_am_read() reads and Amiga joystick data. - */ - -static int js_am_read(void *info, int **axes, int **buttons) -{ - int data = 0; - - switch (*(int*)info) { - case 0: - data = ~custom.joy0dat; - buttons[0][0] = (~ciaa.pra >> 6) & 1; - break; - - case 1: - data = ~custom.joy1dat; - buttons[0][0] = (~ciaa.pra >> 7) & 1; - break; - - default: - return -1; - } - - axes[0][0] = ((data >> 1) & 1) - ((data >> 9) & 1); - data = ~(data ^ (data << 1)); - axes[0][1] = ((data >> 1) & 1) - ((data >> 9) & 1); - - return 0; -} - -/* - * js_am_init_corr() initializes correction values of - * Amiga joysticks. - */ - -static void __init js_am_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } -} - -#ifndef MODULE -int __init js_am_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_am[i] = ints[i+1]; - return 1; -} -__setup("js_am=", js_am_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_am_init(void) -#endif -{ - int i; - - for (i = 0; i < 2; i++) - if (js_am[i]) { - js_am_port = js_register_port(js_am_port, &i, 1, sizeof(int), js_am_read); - printk(KERN_INFO "js%d: Amiga joystick at joy%ddat\n", - js_register_device(js_am_port, 0, 2, 1, "Amiga joystick", THIS_MODULE, NULL, NULL), i); - js_am_init_corr(js_am_port->corr); - } - if (js_am_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-amiga: no joysticks specified\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - while (js_am_port) { - if (js_am_port->devs[0]) - js_unregister_device(js_am_port->devs[0]); - js_am_port = js_unregister_port(js_am_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-analog.c b/drivers/char/joystick/joy-analog.c deleted file mode 100644 index f73ee8ded..000000000 --- a/drivers/char/joystick/joy-analog.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * joy-analog.c Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * up to two analog (or CHF/FCS) joysticks on a single joystick port. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/param.h> -#include <asm/system.h> -#include <linux/config.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_AN_MAX_TIME 3000 /* 3 ms */ -#define JS_AN_LOOP_TIME 2000 /* 2 t */ - -static int js_an_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_an_port __initdata = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_an, "2-24i"); - -static int __initdata js_an[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -#include "joy-analog.h" - -struct js_ax_info { - int io; - int speed; - int loop; - int timeout; - struct js_an_info an; -}; - -/* - * Time macros. - */ - -#ifdef __i386__ -#ifdef CONFIG_X86_TSC -#define GET_TIME(x) __asm__ __volatile__ ( "rdtsc" : "=a" (x) : : "dx" ) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "TSC" -#else -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) -#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) -#define TIME_NAME "PIT" -#endif -#elif __alpha__ -#define GET_TIME(x) __asm__ __volatile__ ( "rpcc %0" : "=r" (x) ) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "PCC" -#endif - -#ifndef GET_TIME -#define FAKE_TIME -static unsigned long js_an_faketime = 0; -#define GET_TIME(x) do { x = js_an_faketime++; } while(0) -#define DELTA(x,y) ((x)-(y)) -#define TIME_NAME "Unreliable" -#endif - -/* - * js_an_read() reads analog joystick data. - */ - -static int js_an_read(void *xinfo, int **axes, int **buttons) -{ - struct js_ax_info *info = xinfo; - struct js_an_info *an = &info->an; - int io = info->io; - unsigned long flags; - unsigned char buf[4]; - unsigned int time[4]; - unsigned char u, v, w; - unsigned int p, q, r, s, t; - int i, j; - - an->buttons = ~inb(io) >> 4; - - i = 0; - w = ((an->mask[0] | an->mask[1]) & JS_AN_AXES_STD) | (an->extensions & JS_AN_HAT_FCS) - | ((an->extensions & JS_AN_BUTTONS_PXY_XY) >> 2) | ((an->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - p = info->loop; - q = info->timeout; - - __save_flags(flags); - __cli(); - outb(0xff,io); - GET_TIME(r); - __restore_flags(flags); - t = r; - v = w; - do { - s = t; - u = v; - __cli(); - v = inb(io) & w; - GET_TIME(t); - __restore_flags(flags); - if ((u ^ v) && (DELTA(t,s) < p)) { - time[i] = t; - buf[i] = u ^ v; - i++; - } - } while (v && (i < 4) && (DELTA(t,r) < q)); - - v <<= 4; - - for (--i; i >= 0; i--) { - v |= buf[i]; - for (j = 0; j < 4; j++) - if (buf[i] & (1 << j)) an->axes[j] = (DELTA(time[i],r) << 10) / info->speed; - } - - js_an_decode(an, axes, buttons); - - return -(v != w); -} - -/* - * js_an_calibrate_timer() calibrates the timer and computes loop - * and timeout values for a joystick port. - */ - -static void __init js_an_calibrate_timer(struct js_ax_info *info) -{ - unsigned int i, t, tx, t1, t2, t3; - unsigned long flags; - int io = info->io; - - save_flags(flags); - cli(); - GET_TIME(t1); -#ifdef FAKE_TIME - js_an_faketime += 830; -#endif - udelay(1000); - GET_TIME(t2); - GET_TIME(t3); - restore_flags(flags); - - info->speed = DELTA(t2, t1) - DELTA(t3, t2); - - tx = 1 << 30; - - for(i = 0; i < 50; i++) { - save_flags(flags); - cli(); - GET_TIME(t1); - for(t = 0; t < 50; t++) { inb(io); GET_TIME(t2); } - GET_TIME(t3); - restore_flags(flags); - udelay(i); - if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; - } - - info->loop = (JS_AN_LOOP_TIME * t) / 50000; - info->timeout = (JS_AN_MAX_TIME * info->speed) / 1000; -} - -/* - * js_an_probe() probes for analog joysticks. - */ - -static struct js_port __init *js_an_probe(int io, int mask0, int mask1, struct js_port *port) -{ - struct js_ax_info info, *ax; - int i, numdev; - unsigned char u; - - if (io < 0) return port; - - if (check_region(io, 1)) return port; - - outb(0xff,io); - u = inb(io); - udelay(JS_AN_MAX_TIME); - u = (inb(io) ^ u) & u; - - if (!u) return port; - if (u & 0xf0) return port; - - if ((numdev = js_an_probe_devs(&info.an, u, mask0, mask1, port)) <= 0) - return port; - - info.io = io; - js_an_calibrate_timer(&info); - - request_region(info.io, 1, "joystick (analog)"); - port = js_register_port(port, &info, numdev, sizeof(struct js_ax_info), js_an_read); - ax = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s at %#x ["TIME_NAME" timer, %d %sHz clock, %d ns res]\n", - js_register_device(port, i, js_an_axes(i, &ax->an), js_an_buttons(i, &ax->an), - js_an_name(i, &ax->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &ax->an), - ax->io, - ax->speed > 10000 ? (ax->speed + 800) / 1000 : ax->speed, - ax->speed > 10000 ? "M" : "k", - ax->loop * 1000000000 / JS_AN_LOOP_TIME / ax->speed); - - js_an_read(ax, port->axes, port->buttons); - js_an_init_corr(&ax->an, port->axes, port->corr, 8); - - return port; -} - -#ifndef MODULE -int __init js_an_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_an[i] = ints[i+1]; - return 1; -} -__setup("js_an=", js_an_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_an_init(void) -#endif -{ - int i; - - if (js_an[0] >= 0) { - for (i = 0; (js_an[i*3] >= 0) && i < 8; i++) - js_an_port = js_an_probe(js_an[i*3], js_an[i*3+1], js_an[i*3+2], js_an_port); - } else { - for (i = 0; js_an_port_list[i]; i++) - js_an_port = js_an_probe(js_an_port_list[i], 0, 0, js_an_port); - } - if (js_an_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-analog: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_ax_info *info; - - while (js_an_port) { - for (i = 0; i < js_an_port->ndevs; i++) - if (js_an_port->devs[i]) - js_unregister_device(js_an_port->devs[i]); - info = js_an_port->info; - release_region(info->io, 1); - js_an_port = js_unregister_port(js_an_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-analog.h b/drivers/char/joystick/joy-analog.h deleted file mode 100644 index a1644350c..000000000 --- a/drivers/char/joystick/joy-analog.h +++ /dev/null @@ -1,287 +0,0 @@ -/* - * joy-analog.h Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This file is designed to be included in any joystick driver - * that communicates with standard analog joysticks. This currently - * is: joy-analog.c, joy-assassin.c, and joy-lightning.c - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/bitops.h> - -#define JS_AN_AXES_STD 0x0f -#define JS_AN_BUTTONS_STD 0xf0 - -#define JS_AN_BUTTONS_CHF 0x01 -#define JS_AN_HAT1_CHF 0x02 -#define JS_AN_HAT2_CHF 0x04 -#define JS_AN_ANY_CHF 0x07 -#define JS_AN_HAT_FCS 0x08 -#define JS_AN_HATS_ALL 0x0e -#define JS_AN_BUTTON_PXY_X 0x10 -#define JS_AN_BUTTON_PXY_Y 0x20 -#define JS_AN_BUTTON_PXY_U 0x40 -#define JS_AN_BUTTON_PXY_V 0x80 -#define JS_AN_BUTTONS_PXY_XY 0x30 -#define JS_AN_BUTTONS_PXY_UV 0xc0 -#define JS_AN_BUTTONS_PXY 0xf0 - -static struct { - int x; - int y; -} js_an_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1, 0}, { 0, 1}, {-1, 0}}; - -struct js_an_info { - unsigned char mask[2]; - unsigned int extensions; - int axes[4]; - int initial[4]; - unsigned char buttons; -}; - -/* - * js_an_decode() decodes analog joystick data. - */ - -static void js_an_decode(struct js_an_info *info, int **axes, int **buttons) -{ - int i, j, k; - int hat1, hat2, hat3; - - hat1 = hat2 = hat3 = 0; - if (info->mask[0] & JS_AN_BUTTONS_STD) buttons[0][0] = 0; - if (info->mask[1] & JS_AN_BUTTONS_STD) buttons[1][0] = 0; - - if (info->extensions & JS_AN_ANY_CHF) { - switch (info->buttons & 0xf) { - case 0x1: buttons[0][0] = 0x01; break; - case 0x2: buttons[0][0] = 0x02; break; - case 0x4: buttons[0][0] = 0x04; break; - case 0x8: buttons[0][0] = 0x08; break; - case 0x5: buttons[0][0] = 0x10; break; - case 0x9: buttons[0][0] = 0x20; break; - case 0xf: hat1 = 1; break; - case 0xb: hat1 = 2; break; - case 0x7: hat1 = 3; break; - case 0x3: hat1 = 4; break; - case 0xe: hat2 = 1; break; - case 0xa: hat2 = 2; break; - case 0x6: hat2 = 3; break; - case 0xc: hat2 = 4; break; - } - k = info->extensions & JS_AN_BUTTONS_CHF ? 6 : 4; - } else { - for (i = 1; i >= 0; i--) - for (j = k = 0; j < 4; j++) - if (info->mask[i] & (0x10 << j)) - buttons[i][0] |= ((info->buttons >> j) & 1) << k++; - } - - if (info->extensions & JS_AN_BUTTON_PXY_X) - buttons[0][0] |= (info->axes[2] < (info->initial[2] >> 1)) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_Y) - buttons[0][0] |= (info->axes[3] < (info->initial[3] >> 1)) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_U) - buttons[0][0] |= (info->axes[2] > (info->initial[2] + (info->initial[2] >> 1))) << k++; - if (info->extensions & JS_AN_BUTTON_PXY_V) - buttons[0][0] |= (info->axes[3] > (info->initial[3] + (info->initial[3] >> 1))) << k++; - - if (info->extensions & JS_AN_HAT_FCS) - for (j = 0; j < 4; j++) - if (info->axes[3] < ((info->initial[3] * ((j << 1) + 1)) >> 3)) { - hat3 = j + 1; - break; - } - - for (i = 1; i >= 0; i--) - for (j = k = 0; j < 4; j++) - if (info->mask[i] & (1 << j)) - axes[i][k++] = info->axes[j]; - - if (info->extensions & JS_AN_HAT1_CHF) { - axes[0][k++] = js_an_hat_to_axis[hat1].x; - axes[0][k++] = js_an_hat_to_axis[hat1].y; - } - if (info->extensions & JS_AN_HAT2_CHF) { - axes[0][k++] = js_an_hat_to_axis[hat2].x; - axes[0][k++] = js_an_hat_to_axis[hat2].y; - } - if (info->extensions & JS_AN_HAT_FCS) { - axes[0][k++] = js_an_hat_to_axis[hat3].x; - axes[0][k++] = js_an_hat_to_axis[hat3].y; - } -} - - -/* - * js_an_init_corr() initializes the correction values for - * analog joysticks. - */ - -static void __init js_an_init_corr(struct js_an_info *info, int **axes, struct js_corr **corr, int prec) -{ - int i, j, t; - - for (i = 0; i < 2; i++) - for (j = 0; j < hweight8(info->mask[i] & 0xf); j++) { - - if ((j == 2 && (info->mask[i] & 0xb) == 0xb) || - (j == 3 && (info->mask[i] & 0xf) == 0xf)) { - t = (axes[i][0] + axes[i][1]) >> 1; - } else { - t = axes[i][j]; - } - - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = prec; - corr[i][j].coef[0] = t - (t >> 3); - corr[i][j].coef[1] = t + (t >> 3); - corr[i][j].coef[2] = (1 << 29) / (t - (t >> 2) + 1); - corr[i][j].coef[3] = (1 << 29) / (t - (t >> 2) + 1); - } - - i = hweight8(info->mask[0] & 0xf); - - for (j = i; j < i + (hweight8(info->extensions & JS_AN_HATS_ALL) << 1); j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); - } - - for (i = 0; i < 4; i++) - info->initial[i] = info->axes[i]; -} - - -/* - * js_an_probe_devs() probes for analog joysticks. - */ - -static int __init js_an_probe_devs(struct js_an_info *info, int exist, int mask0, int mask1, struct js_port *port) -{ - info->mask[0] = info->mask[1] = info->extensions = 0; - - if (mask0 || mask1) { - info->mask[0] = mask0 & (exist | 0xf0); - info->mask[1] = mask1 & (exist | 0xf0) & ~info->mask[0]; - info->extensions = (mask0 >> 8) & ((exist & JS_AN_HAT_FCS) | ((exist << 2) & JS_AN_BUTTONS_PXY_XY) | - ((exist << 4) & JS_AN_BUTTONS_PXY_UV) | JS_AN_ANY_CHF); - - if (info->extensions & JS_AN_BUTTONS_PXY) { - info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_XY) >> 2); - info->mask[0] &= ~((info->extensions & JS_AN_BUTTONS_PXY_UV) >> 4); - info->mask[1] = 0; - } - if (info->extensions & JS_AN_HAT_FCS) { - info->mask[0] &= ~JS_AN_HAT_FCS; - info->mask[1] = 0; - info->extensions &= ~(JS_AN_BUTTON_PXY_Y | JS_AN_BUTTON_PXY_V); - } - if (info->extensions & JS_AN_ANY_CHF) { - info->mask[0] |= 0xf0; - info->mask[1] = 0; - } - if (!(info->mask[0] | info->mask[1])) return -1; - } else { - switch (exist) { - case 0x0: - return -1; - case 0x3: - info->mask[0] = 0xf3; /* joystick 0, assuming 4-button */ - break; - case 0xb: - info->mask[0] = 0xfb; /* 3-axis, 4-button joystick */ - break; - case 0xc: - info->mask[0] = 0xcc; /* joystick 1 */ - break; - case 0xf: - info->mask[0] = 0xff; /* 4-axis 4-button joystick */ - break; - default: - printk(KERN_WARNING "joy-analog: Unknown joystick device detected " - "(data=%#x), contact <vojtech@suse.cz>\n", exist); - return -1; - } - } - - return !!info->mask[0] + !!info->mask[1]; -} - -/* - * js_an_axes() returns the number of axes for an analog joystick. - */ - -static inline int js_an_axes(int i, struct js_an_info *info) -{ - return hweight8(info->mask[i] & 0x0f) + hweight8(info->extensions & JS_AN_HATS_ALL) * 2; -} - -/* - * js_an_buttons() returns the number of buttons for an analog joystick. - */ - -static inline int js_an_buttons(int i, struct js_an_info *info) -{ - return hweight8(info->mask[i] & 0xf0) + - (info->extensions & JS_AN_BUTTONS_CHF) * 2 + - hweight8(info->extensions & JS_AN_BUTTONS_PXY); -} - -/* - * js_an_name() constructs a name for an analog joystick. - */ - -static char js_an_name_buf[128] __initdata = ""; - -static char __init *js_an_name(int i, struct js_an_info *info) -{ - - sprintf(js_an_name_buf, "Analog %d-axis %d-button", - hweight8(info->mask[i] & 0x0f), - js_an_buttons(i, info)); - - if (info->extensions & JS_AN_HATS_ALL) - sprintf(js_an_name_buf, "%s %d-hat", - js_an_name_buf, - hweight8(info->extensions & JS_AN_HATS_ALL)); - - strcat(js_an_name_buf, " joystick"); - - if (info->extensions) - sprintf(js_an_name_buf, "%s with%s%s%s extensions", - js_an_name_buf, - info->extensions & JS_AN_ANY_CHF ? " CHF" : "", - info->extensions & JS_AN_HAT_FCS ? " FCS" : "", - info->extensions & JS_AN_BUTTONS_PXY ? " XY/UV" : ""); - - return js_an_name_buf; -} diff --git a/drivers/char/joystick/joy-assassin.c b/drivers/char/joystick/joy-assassin.c deleted file mode 100644 index 469c6c8ce..000000000 --- a/drivers/char/joystick/joy-assassin.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * joy-assassin.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * joysticks using FP-Gaming's Assassin 3D protocol. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_AS_MAX_START 1000 -#define JS_AS_DELAY_READ 3000 -#define JS_AS_MAX_LENGTH 40 - -#define JS_AS_MODE_A3D 1 /* Assassin 3D */ -#define JS_AS_MODE_PAN 2 /* Panther */ -#define JS_AS_MODE_OEM 3 /* Panther OEM version */ -#define JS_AS_MODE_PXL 4 /* Panther XL */ - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_as, "2-24i"); - -static int __initdata js_as[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -static int js_as_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_as_port __initdata = NULL; - -#include "joy-analog.h" - -struct js_as_info { - int io; - char mode; - char rudder; - struct js_an_info an; -}; - -/* - * js_as_read_packet() reads an Assassin 3D packet. - */ - -static int js_as_read_packet(int io, int length, char *data) -{ - unsigned char u, v; - int i; - unsigned int t, p; - unsigned long flags; - - i = 0; - - __save_flags(flags); - __cli(); - - outb(0xff,io); - v = inb(io); - t = p = JS_AS_MAX_START; - - while (t > 0 && i < length) { - t--; - u = v; v = inb(io); - if (~v & u & 0x10) { - data[i++] = v >> 5; - p = t = (p - t) << 3; - } - } - - __restore_flags(flags); - - return i; -} - -/* - * js_as_csum() computes checksum of triplet packet - */ - -static int js_as_csum(char *data, int count) -{ - int i, csum = 0; - for (i = 0; i < count - 2; i++) csum += data[i]; - return (csum & 0x3f) != ((data[count - 2] << 3) | data[count - 1]); -} - -/* - * js_as_read() reads and analyzes A3D joystick data. - */ - -static int js_as_read(void *xinfo, int **axes, int **buttons) -{ - struct js_as_info *info = xinfo; - char data[JS_AS_MAX_LENGTH]; - - switch (info->mode) { - - case JS_AS_MODE_A3D: - case JS_AS_MODE_OEM: - case JS_AS_MODE_PAN: - - if (js_as_read_packet(info->io, 29, data) != 29) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 29)) return -1; - - axes[0][0] = ((data[5] << 6) | (data[6] << 3) | data[ 7]) - ((data[5] & 4) << 7); - axes[0][1] = ((data[8] << 6) | (data[9] << 3) | data[10]) - ((data[8] & 4) << 7); - - buttons[0][0] = (data[2] << 2) | (data[3] >> 1); - - info->an.axes[0] = ((char)((data[11] << 6) | (data[12] << 3) | (data[13]))) + 128; - info->an.axes[1] = ((char)((data[14] << 6) | (data[15] << 3) | (data[16]))) + 128; - info->an.axes[2] = ((char)((data[17] << 6) | (data[18] << 3) | (data[19]))) + 128; - info->an.axes[3] = ((char)((data[20] << 6) | (data[21] << 3) | (data[22]))) + 128; - - info->an.buttons = ((data[3] << 3) | data[4]) & 0xf; - - js_an_decode(&info->an, axes + 1, buttons + 1); - - return 0; - - case JS_AS_MODE_PXL: - - if (js_as_read_packet(info->io, 33, data) != 33) return -1; - if (data[0] != info->mode) return -1; - if (js_as_csum(data, 33)) return -1; - - axes[0][0] = ((char)((data[15] << 6) | (data[16] << 3) | (data[17]))) + 128; - axes[0][1] = ((char)((data[18] << 6) | (data[19] << 3) | (data[20]))) + 128; - info->an.axes[0] = ((char)((data[21] << 6) | (data[22] << 3) | (data[23]))) + 128; - axes[0][2] = ((char)((data[24] << 6) | (data[25] << 3) | (data[26]))) + 128; - - axes[0][3] = ( data[5] & 1) - ((data[5] >> 2) & 1); - axes[0][4] = ((data[5] >> 1) & 1) - ((data[6] >> 2) & 1); - axes[0][5] = ((data[4] >> 1) & 1) - ( data[3] & 1); - axes[0][6] = ((data[4] >> 2) & 1) - ( data[4] & 1); - - axes[0][7] = ((data[ 9] << 6) | (data[10] << 3) | data[11]) - ((data[ 9] & 4) << 7); - axes[0][8] = ((data[12] << 6) | (data[13] << 3) | data[14]) - ((data[12] & 4) << 7); - - buttons[0][0] = (data[2] << 8) | ((data[3] & 6) << 5) | (data[7] << 3) | data[8]; - - if (info->rudder) axes[1][0] = info->an.axes[0]; - - return 0; - } - return -1; -} - -/* - * js_as_pxl_init_corr() initializes the correction values for - * the Panther XL. - */ - -static void __init js_as_pxl_init_corr(struct js_corr **corr, int **axes) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = axes[0][i] - 4; - corr[0][i].coef[1] = axes[0][i] + 4; - corr[0][i].coef[2] = (1 << 29) / (127 - 32); - corr[0][i].coef[3] = (1 << 29) / (127 - 32); - } - - corr[0][2].type = JS_CORR_BROKEN; - corr[0][2].prec = 0; - corr[0][2].coef[0] = 127 - 4; - corr[0][2].coef[1] = 128 + 4; - corr[0][2].coef[2] = (1 << 29) / (127 - 6); - corr[0][2].coef[3] = (1 << 29) / (127 - 6); - - for (i = 3; i < 7; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } - - for (i = 7; i < 9; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_as_init_corr() initializes the correction values for - * the Panther and Assassin. - */ - -static void __init js_as_as_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = -1; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (104 << 14); - corr[0][i].coef[3] = (104 << 14); - } -} - -/* - * js_as_rudder_init_corr() initializes the correction values for - * the Panther XL connected rudder. - */ - -static void __init js_as_rudder_init_corr(struct js_corr **corr, int **axes) -{ - corr[1][0].type = JS_CORR_BROKEN; - corr[1][0].prec = 0; - corr[1][0].coef[0] = axes[1][0] - (axes[1][0] >> 3); - corr[1][0].coef[1] = axes[1][0] + (axes[1][0] >> 3); - corr[1][0].coef[2] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); - corr[1][0].coef[3] = (1 << 29) / (axes[1][0] - (axes[1][0] >> 2) + 1); -} - -/* - * js_as_probe() probes for A3D joysticks. - */ - -static struct js_port __init *js_as_probe(int io, int mask0, int mask1, struct js_port *port) -{ - struct js_as_info iniinfo; - struct js_as_info *info = &iniinfo; - char *name; - char data[JS_AS_MAX_LENGTH]; - unsigned char u; - int i; - int numdev; - - memset(info, 0, sizeof(struct js_as_info)); - - if (io < 0) return port; - - if (check_region(io, 1)) return port; - - i = js_as_read_packet(io, JS_AS_MAX_LENGTH, data); - - printk("%d\n", i); - - if (!i) return port; - if (js_as_csum(data, i)) return port; - - if (data[0] && data[0] <= 4) { - info->mode = data[0]; - info->io = io; - request_region(io, 1, "joystick (assassin)"); - port = js_register_port(port, info, 3, sizeof(struct js_as_info), js_as_read); - info = port->info; - } else { - printk(KERN_WARNING "joy-assassin: unknown joystick device detected " - "(io=%#x, id=%d), contact <vojtech@suse.cz>\n", io, data[0]); - return port; - } - - udelay(JS_AS_DELAY_READ); - - if (info->mode == JS_AS_MODE_PXL) { - printk(KERN_INFO "js%d: MadCatz Panther XL at %#x\n", - js_register_device(port, 0, 9, 9, "MadCatz Panther XL", THIS_MODULE, NULL, NULL), - info->io); - js_as_read(port->info, port->axes, port->buttons); - js_as_pxl_init_corr(port->corr, port->axes); - if (info->an.axes[0] < 254) { - printk(KERN_INFO "js%d: Analog rudder on MadCatz Panther XL\n", - js_register_device(port, 1, 1, 0, "Analog rudder", THIS_MODULE, NULL, NULL)); - info->rudder = 1; - port->axes[1][0] = info->an.axes[0]; - js_as_rudder_init_corr(port->corr, port->axes); - } - return port; - } - - switch (info->mode) { - case JS_AS_MODE_A3D: name = "FP-Gaming Assassin 3D"; break; - case JS_AS_MODE_PAN: name = "MadCatz Panther"; break; - case JS_AS_MODE_OEM: name = "OEM Assassin 3D"; break; - default: name = "This cannot happen"; break; - } - - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, 0, 2, 3, name, THIS_MODULE, NULL, NULL), - name, info->io); - - js_as_as_init_corr(port->corr); - - js_as_read(port->info, port->axes, port->buttons); - - for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 254) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i + 1, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &info->an), name); - - js_an_decode(&info->an, port->axes + 1, port->buttons + 1); - js_an_init_corr(&info->an, port->axes + 1, port->corr + 1, 0); - - return port; -} - -#ifndef MODULE -int __init js_as_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_as[i] = ints[i+1]; - return 1; -} -__setup("js_as=", js_as_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_as_init(void) -#endif -{ - int i; - - if (js_as[0] >= 0) { - for (i = 0; (js_as[i*3] >= 0) && i < 8; i++) - js_as_port = js_as_probe(js_as[i*3], js_as[i*3+1], js_as[i*3+2], js_as_port); - } else { - for (i = 0; js_as_port_list[i]; i++) js_as_port = js_as_probe(js_as_port_list[i], 0, 0, js_as_port); - } - if (js_as_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-assassin: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_as_info *info; - - while (js_as_port) { - for (i = 0; i < js_as_port->ndevs; i++) - if (js_as_port->devs[i]) - js_unregister_device(js_as_port->devs[i]); - info = js_as_port->info; - release_region(info->io, 1); - js_as_port = js_unregister_port(js_as_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-console.c b/drivers/char/joystick/joy-console.c deleted file mode 100644 index e56da540a..000000000 --- a/drivers/char/joystick/joy-console.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - * joy-console.c Version 0.14V - * - * Copyright (c) 1998 Andree Borrmann - * Copyright (c) 1999 John Dahlstrom - * Copyright (c) 1999 David Kuder - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * console (NES, SNES, N64, Multi1, Multi2, PSX) gamepads - * connected via parallel port. Up to five such controllers - * can be connected to one parallel port. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_console, "2-6i"); -MODULE_PARM(js_console_2,"2-6i"); -MODULE_PARM(js_console_3,"2-6i"); - - -#define JS_NO_PAD 0 -#define JS_SNES_PAD 1 -#define JS_NES_PAD 2 -#define JS_NES4_PAD 3 -#define JS_MULTI_STICK 4 -#define JS_MULTI2_STICK 5 -#define JS_PSX_PAD 6 -#define JS_N64_PAD 7 -#define JS_N64_PAD_DPP 8 /* DirectPad Pro compatible layout */ - -#define JS_MAX_PAD JS_N64_PAD_DPP - -struct js_console_info { - struct pardevice *port; /* parport device */ - int pads; /* total number of pads */ - int pad_to_device[5]; /* pad to js device mapping (js0, js1, etc.) */ - int snes; /* SNES pads */ - int nes; /* NES pads */ - int n64; /* N64 pads */ - int n64_dpp; /* bits indicate N64 pads treated 14 button, 2 axis */ - int multi; /* Multi joysticks */ - int multi2; /* Multi joysticks with 2 buttons */ - int psx; /* PSX controllers */ -}; - -static struct js_port* js_console_port = NULL; - -static int js_console[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console_2[] __initdata = { -1, 0, 0, 0, 0, 0 }; -static int js_console_3[] __initdata = { -1, 0, 0, 0, 0, 0 }; - -static int status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; - -/* - * NES/SNES support. - */ - -#define JS_NES_DELAY 6 /* Delay between bits - 6us */ - -#define JS_NES_LENGTH 8 /* The NES pads use 8 bits of data */ - -#define JS_NES_A 0 -#define JS_NES_B 1 -#define JS_NES_START 2 -#define JS_NES_SELECT 3 -#define JS_NES_UP 4 -#define JS_NES_DOWN 5 -#define JS_NES_LEFT 6 -#define JS_NES_RIGHT 7 - -#define JS_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ - -#define JS_SNES_B 0 -#define JS_SNES_Y 1 -#define JS_SNES_START 2 -#define JS_SNES_SELECT 3 -#define JS_SNES_UP 4 -#define JS_SNES_DOWN 5 -#define JS_SNES_LEFT 6 -#define JS_SNES_RIGHT 7 -#define JS_SNES_A 8 -#define JS_SNES_X 9 -#define JS_SNES_L 10 -#define JS_SNES_R 11 - -#define JS_NES_POWER 0xfc -#define JS_NES_CLOCK 0x01 -#define JS_NES_LATCH 0x02 - -/* - * js_nes_read_packet() reads a NES/SNES packet. - * Each pad uses one bit per byte. So all pads connected to - * this port are read in parallel. - */ - -static void js_nes_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i; - - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK | JS_NES_LATCH, info->port); - udelay(JS_NES_DELAY * 2); - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); - - for (i = 0; i < length; i++) { - udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER, info->port); - data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - udelay(JS_NES_DELAY); - JS_PAR_DATA_OUT(JS_NES_POWER | JS_NES_CLOCK, info->port); - } -} - -/* - * N64 support. - */ - -#define JS_N64_A 0 -#define JS_N64_B 1 -#define JS_N64_Z 2 -#define JS_N64_START 3 -#define JS_N64_UP 4 -#define JS_N64_DOWN 5 -#define JS_N64_LEFT 6 -#define JS_N64_RIGHT 7 -#define JS_N64_UNUSED1 8 -#define JS_N64_UNUSED2 9 -#define JS_N64_L 10 -#define JS_N64_R 11 -#define JS_N64_CU 12 -#define JS_N64_CD 13 -#define JS_N64_CL 14 -#define JS_N64_CR 15 -#define JS_N64_X 23 /* 16 - 23, signed 8-bit int */ -#define JS_N64_Y 31 /* 24 - 31, signed 8-bit int */ - -#define JS_N64_LENGTH 32 /* N64 bit length, not including stop bit */ -#define JS_N64_REQUEST_LENGTH 37 /* transmit request sequence is 9 bits long */ -#define JS_N64_DELAY 133 /* delay between transmit request, and response ready (us) */ -#define JS_N64_REQUEST 0x1dd1111111ULL /* the request data command (encoded for 000000011) */ -#define JS_N64_DWS 3 /* delay between write segments (required for sound playback because of ISA DMA) */ - /* JS_N64_DWS > 24 is known to fail */ -#define JS_N64_POWER_W 0xe2 /* power during write (transmit request) */ -#define JS_N64_POWER_R 0xfd /* power during read */ -#define JS_N64_OUT 0x1d /* output bits to the 4 pads */ - /* Reading the main axes of any N64 pad is known to fail if the corresponding bit */ - /* in JS_N64_OUT is pulled low on the output port (by any routine) for more */ - /* than 0.123 consecutive ms */ -#define JS_N64_CLOCK 0x02 /* clock bits for read */ - -/* - * js_n64_read_packet() reads an N64 packet. - * Each pad uses one bit per byte. So all pads connected to this port are read in parallel. - */ - -static void js_n64_read_packet(struct js_console_info *info, unsigned char *data) -{ - int i; - unsigned long flags; - -/* - * Request the pad to transmit data - */ - - save_flags(flags); - cli(); - for (i = 0; i < JS_N64_REQUEST_LENGTH; i++) { - JS_PAR_DATA_OUT(JS_N64_POWER_W | ((JS_N64_REQUEST >> i) & 1 ? JS_N64_OUT : 0), info->port); - udelay(JS_N64_DWS); - } - restore_flags(flags); - -/* - * Wait for the pad response to be loaded into the 33-bit register of the adapter - */ - - udelay(JS_N64_DELAY); - -/* - * Grab data (ignoring the last bit, which is a stop bit) - */ - - for (i = 0; i < JS_N64_LENGTH; i++) { - JS_PAR_DATA_OUT(JS_N64_POWER_R, info->port); - data[i] = JS_PAR_STATUS(info->port); - JS_PAR_DATA_OUT(JS_N64_POWER_R | JS_N64_CLOCK, info->port); - } - -/* - * We must wait ~0.2 ms here for the controller to reinitialize before the next read request. - * No worries as long as js_console_read is polled less frequently than this. - */ - -} - -/* - * Multisystem joystick support - */ - -#define JS_MULTI_LENGTH 5 /* Multi system joystick packet lenght is 5 */ -#define JS_MULTI2_LENGTH 6 /* One more bit for one more button */ - -#define JS_MULTI_UP 0 -#define JS_MULTI_DOWN 1 -#define JS_MULTI_LEFT 2 -#define JS_MULTI_RIGHT 3 -#define JS_MULTI_BUTTON 4 -#define JS_MULTI_BUTTON2 5 - -/* - * js_multi_read_packet() reads a Multisystem joystick packet. - */ - -static void js_multi_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i; - - for (i = 0; i < length; i++) { - JS_PAR_DATA_OUT(~(1 << i), info->port); - data[i] = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - printk(" %d", data[i]); - } - printk("\n"); -} - -/* - * PSX support - */ - -#define JS_PSX_DELAY 10 -#define JS_PSX_LENGTH 8 /* talk to the controller in bytes */ - -#define JS_PSX_NORMAL 0x41 /* Standard Digital controller */ -#define JS_PSX_NEGCON 0x23 /* NegCon pad */ -#define JS_PSX_MOUSE 0x12 /* PSX Mouse */ -#define JS_PSX_ANALOGR 0x73 /* Analog controller in Red mode */ -#define JS_PSX_ANALOGG 0x53 /* Analog controller in Green mode */ - -#define JS_PSX_JOYR 0x02 /* These are for the Analog/Dual Shock controller in RED mode */ -#define JS_PSX_JOYL 0x04 /* I'm not sure the exact purpose of these but its in the docs */ -#define JS_PSX_SELBUT 0x01 /* Standard buttons on almost all PSX controllers. */ -#define JS_PSX_START 0x08 -#define JS_PSX_UP 0x10 /* Digital direction pad */ -#define JS_PSX_RIGHT 0x20 -#define JS_PSX_DOWN 0x40 -#define JS_PSX_LEFT 0x80 - -#define JS_PSX_CLOCK 0x04 /* Pin 3 */ -#define JS_PSX_COMMAND 0x01 /* Pin 1 */ -#define JS_PSX_POWER 0xf8 /* Pins 5-9 */ -#define JS_PSX_SELECT 0x02 /* Pin 2 */ -#define JS_PSX_NOPOWER 0x04 - -/* - * js_psx_command() writes 8bit command and reads 8bit data from - * the psx pad. - */ - -static int js_psx_command(struct js_console_info *info, int b) -{ - int i, cmd, ret=0; - - cmd = (b&1)?JS_PSX_COMMAND:0; - for (i=0; i<8; i++) { - JS_PAR_DATA_OUT(cmd | JS_PSX_POWER, info->port); - udelay(JS_PSX_DELAY); - ret |= ((JS_PAR_STATUS(info->port) ^ JS_PAR_STATUS_INVERT ) & info->psx) ? (1<<i) : 0; - cmd = (b&1)?JS_PSX_COMMAND:0; - JS_PAR_DATA_OUT(cmd | JS_PSX_CLOCK | JS_PSX_POWER, info->port); - udelay(JS_PSX_DELAY); - b >>= 1; - } - return ret; -} - -/* - * js_psx_read_packet() reads a whole psx packet and returns - * device identifier code. - */ - -static int js_psx_read_packet(struct js_console_info *info, int length, unsigned char *data) -{ - int i, ret; - unsigned long flags; - - __save_flags(flags); - __cli(); - - JS_PAR_DATA_OUT(JS_PSX_POWER, info->port); - - JS_PAR_DATA_OUT(JS_PSX_CLOCK | JS_PSX_SELECT | JS_PSX_POWER, info->port); /* Select pad */ - udelay(JS_PSX_DELAY*2); - js_psx_command(info, 0x01); /* Access pad */ - ret = js_psx_command(info, 0x42); /* Get device id */ - if (js_psx_command(info, 0)=='Z') /* okay? */ - for (i=0; i<length; i++) - data[i]=js_psx_command(info, 0); - else ret = -1; - - JS_PAR_DATA_OUT(JS_PSX_SELECT | JS_PSX_CLOCK | JS_PSX_POWER, info->port); - __restore_flags(flags); - - return ret; -} - - -/* - * js_console_read() reads and analyzes console pads data. - */ - -#define JS_MAX_LENGTH JS_N64_LENGTH - -static int js_console_read(void *xinfo, int **axes, int **buttons) -{ - struct js_console_info *info = xinfo; - unsigned char data[JS_MAX_LENGTH]; - - int i, j, s; - int n = 0; - -/* - * NES and SNES pads - */ - - if (info->nes || info->snes) { - - js_nes_read_packet(info, info->snes ? JS_SNES_LENGTH : JS_NES_LENGTH, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->nes & s) { - axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); - axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - - buttons[n][0] = (data[JS_NES_A] &s?1:0) | (data[JS_NES_B] &s?2:0) - | (data[JS_NES_START]&s?4:0) | (data[JS_NES_SELECT]&s?8:0); - } else - if (info->snes & s) { - axes[n][0] = (data[JS_SNES_RIGHT]&s?1:0) - (data[JS_SNES_LEFT]&s?1:0); - axes[n][1] = (data[JS_SNES_DOWN] &s?1:0) - (data[JS_SNES_UP] &s?1:0); - - buttons[n][0] = (data[JS_SNES_A] &s?0x01:0) | (data[JS_SNES_B] &s?0x02:0) - | (data[JS_SNES_X] &s?0x04:0) | (data[JS_SNES_Y] &s?0x08:0) - | (data[JS_SNES_L] &s?0x10:0) | (data[JS_SNES_R] &s?0x20:0) - | (data[JS_SNES_START]&s?0x40:0) | (data[JS_SNES_SELECT]&s?0x80:0); - } - } - } - -/* - * N64 pads - */ - - if (info->n64) { - if ( (info->nes || info->snes) && (info->n64 & status_bit[0]) ) { - /* SNES/NES compatibility */ - udelay(240); /* 200 us delay + 20% tolerance */ - } - - js_n64_read_packet(info, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->n64 & s & ~(data[JS_N64_UNUSED1] | data[JS_N64_UNUSED2])) { - - buttons[n][0] = ( ((data[JS_N64_A]&s) ? 0x01:0) | ((data[JS_N64_B] & s ) ? 0x02:0) - | ((data[JS_N64_Z]&s) ? 0x04:0) | ((data[JS_N64_L] & s ) ? 0x08:0) - | ((data[JS_N64_R]&s) ? 0x10:0) | ((data[JS_N64_START]&s)? 0x20:0) - | ((data[JS_N64_CU]&s)? 0x40:0) | ((data[JS_N64_CR]&s) ? 0x80:0) - | ((data[JS_N64_CD]&s)?0x100:0) | ((data[JS_N64_CL]&s) ?0x200:0) ); - - if (info->n64_dpp & s) { - buttons[n][0] |= ((data[JS_N64_LEFT]&s) ? 0x400:0) | ((data[JS_N64_UP] & s)? 0x800:0) - |((data[JS_N64_RIGHT]&s)?0x1000:0) | ((data[JS_N64_DOWN]&s)?0x2000:0); - } else { - axes[n][2] = (data[JS_N64_RIGHT]&s?1:0) - (data[JS_N64_LEFT]&s?1:0); - axes[n][3] = (data[JS_N64_DOWN] &s?1:0) - (data[JS_N64_UP] &s?1:0); - } - - /* build int from bits of signed 8-bit int's */ - j = 7; - axes[n][0] = (data[JS_N64_X - j] & s) ? ~0x7f : 0; - axes[n][1] = (data[JS_N64_Y - j] & s) ? ~0x7f : 0; - while ( j-- > 0 ) { - axes[n][0] |= (data[JS_N64_X - j] & s) ? (1 << j) : 0; - axes[n][1] |= (data[JS_N64_Y - j] & s) ? (1 << j) : 0; - } - /* flip Y-axis for conformity */ - axes[n][1] = -axes[n][1]; - - } - } - } - -/* - * Multi and Multi2 joysticks - */ - - if (info->multi || info->multi2) { - - js_multi_read_packet(info, info->multi2 ? JS_MULTI2_LENGTH : JS_MULTI_LENGTH, data); - - for (i = 0; i < 5; i++) { - s = status_bit[i]; - n = info->pad_to_device[i]; - if (info->multi & s) { - axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); - axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); - - buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0; - } else - if (info->multi2 & s) { - axes[n][0] = (data[JS_MULTI_RIGHT]&s?1:0) - (data[JS_MULTI_LEFT]&s?1:0); - axes[n][1] = (data[JS_MULTI_DOWN] &s?1:0) - (data[JS_MULTI_UP] &s?1:0); - - buttons[n][0] = (data[JS_MULTI_BUTTON]&s)?1:0 | (data[JS_MULTI_BUTTON2]&s)?2:0; - } - } - } - -/* - * PSX controllers - */ - - if (info->psx) { - - for ( i = 0; i < 5; i++ ) - if ( info->psx & status_bit[i] ) { - n = info->pad_to_device[i]; - break; - } - - buttons[n][0] = 0; - - switch (js_psx_read_packet(info, 6, data)) { - - case JS_PSX_ANALOGR: - - buttons[n][0] |= (data[0]&JS_PSX_JOYL?0:0x800) | (data[0]&JS_PSX_JOYR?0:0x400); - - case JS_PSX_ANALOGG: - - axes[n][2] = data[2]; - axes[n][3] = data[3]; - axes[n][4] = data[4]; - axes[n][5] = data[5]; - - case JS_PSX_NORMAL: - case JS_PSX_NEGCON: - - axes[n][0] = (data[0]&JS_PSX_RIGHT?0:1) - (data[0]&JS_PSX_LEFT?0:1); - axes[n][1] = (data[0]&JS_PSX_DOWN ?0:1) - (data[0]&JS_PSX_UP ?0:1); - - buttons[n][0] |= ((~data[1]&0xf)<<4) | ((~data[1]&0xf0)>>4) | - (data[0]&JS_PSX_START?0:0x200) | (data[0]&JS_PSX_SELBUT?0:0x100); - - break; - - } - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: if parport_claim() will sleep we can get into mess. - */ - -int js_console_open(struct js_dev *dev) -{ - struct js_console_info *info = dev->port->info; - if (!MOD_IN_USE && parport_claim(info->port)) return -EBUSY; - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_console_close(struct js_dev *dev) -{ - struct js_console_info *info = dev->port->info; - MOD_DEC_USE_COUNT; - if (!MOD_IN_USE) parport_release(info->port); - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_console_info *info; - int i; - - while (js_console_port) { - for (i = 0; i < js_console_port->ndevs; i++) - if (js_console_port->devs[i]) - js_unregister_device(js_console_port->devs[i]); - info = js_console_port->info; - parport_unregister_device(info->port); - js_console_port = js_unregister_port(js_console_port); - } -} -#endif - -/* - * js_console_init_corr() initializes correction values of - * console gamepads. - */ - -static void __init js_console_init_corr(int num_axes, int type, struct js_corr *corr) -{ - int i; - - for (i = 0; i < num_axes; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - if (type == JS_N64_PAD || type == JS_N64_PAD_DPP) { - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 22); - corr[i].coef[3] = (1 << 22); - } - } - - if (type == JS_PSX_ANALOGG || type == JS_PSX_ANALOGR) { - for (i = 2; i < 6; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 127 - 2; - corr[i].coef[1] = 128 + 2; - corr[i].coef[2] = (1 << 29) / (127 - 4); - corr[i].coef[3] = (1 << 29) / (127 - 4); - } - } -} - -/* - * js_console_probe() probes for console gamepads. - * Only PSX pads can really be probed for. - */ - -static struct js_port __init *js_console_probe(int *config, struct js_port *port) -{ - char *name[5]; - int i, psx, axes[5], buttons[5], type[5]; - unsigned char data[2]; /* used for PSX probe */ - struct js_console_info info; - struct parport *pp; - - memset(&info, 0, sizeof(struct js_console_info)); - - if (config[0] < 0) return port; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-console: no such parport\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (console)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; - - if (parport_claim(info.port)) - { - parport_unregister_device(info.port); /* port currently not available ... */ - return port; - } - - for (i = 0; i < 5; i++) { - - type[info.pads] = config[i+1]; - info.pad_to_device[i] = info.pads; - - switch(config[i+1]) { - - case JS_NO_PAD: - - break; - - case JS_SNES_PAD: - - axes[info.pads] = 2; - buttons[info.pads] = 8; - name[info.pads] = "SNES pad"; - info.snes |= status_bit[i]; - info.pads++; - break; - - case JS_NES_PAD: - - axes[info.pads] = 2; - buttons[info.pads] = 4; - name[info.pads] = "NES pad"; - info.nes |= status_bit[i]; - info.pads++; - break; - - case JS_N64_PAD: - axes[info.pads] = 4; - buttons[info.pads] = 10; - name[info.pads] = "N64 pad"; - info.n64 |= status_bit[i]; - info.pads++; - break; - - case JS_N64_PAD_DPP: - axes[info.pads] = 2; - buttons[info.pads] = 14; - name[info.pads] = "N64 pad (DPP mode)"; - info.n64 |= status_bit[i]; - info.n64_dpp |= status_bit[i]; - info.pads++; - break; - - case JS_MULTI_STICK: - - axes[info.pads] = 2; - buttons[info.pads] = 1; - name[info.pads] = "Multisystem joystick"; - info.multi |= status_bit[i]; - info.pads++; - break; - - case JS_MULTI2_STICK: - - axes[info.pads] = 2; - buttons[info.pads] = 2; - name[info.pads] = "Multisystem joystick (2 fire)"; - info.multi |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_PAD: - - info.psx |= status_bit[i]; - psx = js_psx_read_packet(&info, 2, data); - psx = js_psx_read_packet(&info, 2, data); - info.psx &= ~status_bit[i]; - - type[i] = psx; - - switch(psx) { - case JS_PSX_NORMAL: - axes[info.pads] = 2; - buttons[info.pads] = 10; - name[info.pads] = "PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_ANALOGR: - axes[info.pads] = 6; - buttons[info.pads] = 12; - name[info.pads] = "Analog Red PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_ANALOGG: - axes[info.pads] = 6; - buttons[info.pads] = 10; - name[info.pads] = "Analog Green PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_NEGCON: - axes[info.pads] = 2; - buttons[info.pads] = 10; - name[info.pads] = "NegCon PSX pad"; - info.psx |= status_bit[i]; - info.pads++; - break; - - case JS_PSX_MOUSE: - printk(KERN_WARNING "joy-psx: PSX mouse not supported...\n"); - break; - - case -1: - printk(KERN_ERR "joy-psx: no PSX controller found...\n"); - break; - - default: - printk(KERN_WARNING "joy-psx: PSX controller unknown: 0x%x," - " please report to <vojtech@suse.cz>.\n", psx); - } - break; - - default: - - printk(KERN_WARNING "joy-console: pad type %d unknown\n", config[i+1]); - } - } - - if (!info.pads) { - parport_release(info.port); - parport_unregister_device(info.port); - return port; - } - - port = js_register_port(port, &info, info.pads, sizeof(struct js_console_info), js_console_read); - - for (i = 0; i < info.pads; i++) { - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i, axes[i], buttons[i], name[i], NULL, js_console_open, js_console_close), - name[i], info.port->port->name); - - js_console_init_corr(axes[i], type[i], port->corr[i]); - } - - parport_release(info.port); - return port; -} - -#ifndef MODULE -int __init js_console_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console[i] = ints[i+1]; - return 1; -} -int __init js_console_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console_2[i] = ints[i+1]; - return 1; -} -int __init js_console_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(6); - for (i = 0; i <= ints[0] && i < 6; i++) js_console_3[i] = ints[i+1]; - return 1; -} -__setup("js_console=", js_console_setup); -__setup("js_console_2=", js_console_setup_2); -__setup("js_console_3=", js_console_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_console_init(void) -#endif -{ - js_console_port = js_console_probe(js_console, js_console_port); - js_console_port = js_console_probe(js_console_2, js_console_port); - js_console_port = js_console_probe(js_console_3, js_console_port); - - if (js_console_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-console: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-creative.c b/drivers/char/joystick/joy-creative.c deleted file mode 100644 index b5ae4c019..000000000 --- a/drivers/char/joystick/joy-creative.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * joy-creative.c Version 1.2 - * - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Creative Labs Blaster gamepad family. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_CR_MAX_STROBE 100 /* 100 us max wait for first strobe */ -#define JS_CR_LENGTH 36 - -#define JS_CR_MODE_BGPC 8 - -static int js_cr_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_cr_port __initdata = NULL; - -struct js_cr_info { - int io; - unsigned char mode[2]; -}; - -/* - * js_cr_read_packet() reads a Blaster gamepad packet. - */ - -static int js_cr_read_packet(int io, unsigned int *data) -{ - unsigned long flags; - unsigned char u, v, w; - __u64 buf[2]; - int r[2], t[2], p[2]; - int i, j, ret; - - for (i = 0; i < 2; i++) { - r[i] = buf[i] = 0; - p[i] = t[i] = JS_CR_MAX_STROBE; - p[i] += JS_CR_MAX_STROBE; - } - - __save_flags(flags); - __cli(); - - u = inb(io); - - do { - t[0]--; t[1]--; - v = inb(io); - for (i = 0, w = u ^ v; i < 2 && w; i++, w >>= 2) - if (w & 0x30) { - if ((w & 0x30) < 0x30 && r[i] < JS_CR_LENGTH && t[i] > 0) { - buf[i] |= (__u64)((w >> 5) & 1) << r[i]++; - p[i] = t[i] = (p[i] - t[i]) << 1; - u = v; - } else t[i] = 0; - } - } while (t[0] > 0 || t[1] > 0); - - __restore_flags(flags); - - - ret = 0; - - for (i = 0; i < 2; i++) { - - if (r[i] != JS_CR_LENGTH) continue; - - for (j = 0; j < JS_CR_LENGTH && (buf[i] & 0x04104107f) ^ 0x041041040; j++) - buf[i] = (buf[i] >> 1) | ((__u64)(buf[i] & 1) << (JS_CR_LENGTH - 1)); - - if (j < JS_CR_LENGTH) ret |= (1 << i); - - data[i] = ((buf[i] >> 7) & 0x000001f) | ((buf[i] >> 8) & 0x00003e0) - | ((buf[i] >> 9) & 0x0007c00) | ((buf[i] >> 10) & 0x00f8000) - | ((buf[i] >> 11) & 0x1f00000); - - } - - return ret; -} - -/* - * js_cr_read() reads and analyzes Blaster gamepad data. - */ - -static int js_cr_read(void *xinfo, int **axes, int **buttons) -{ - struct js_cr_info *info = xinfo; - unsigned int data[2]; - int i, r; - - if (!(r = js_cr_read_packet(info->io, data))) - return -1; - - for (i = 0; i < 2; i++) - if (r & (1 << i)) { - switch (info->mode[i]) { - - case JS_CR_MODE_BGPC: - - axes[i][0] = ((data[i] >> 4) & 1) - ((data[i] >> 3) & 1); - axes[i][1] = ((data[i] >> 2) & 1) - ((data[i] >> 1) & 1); - - buttons[i][0] = ((data[i] >> 12) & 0x007) | ((data[i] >> 6) & 0x038) - | ((data[i] >> 1) & 0x0c0) | ((data[i] >> 7) & 0x300) - | ((data[i] << 5) & 0xc00); - - break; - - default: - break; - - } - } - - return 0; -} - -/* - * js_cr_init_corr() initializes correction values of - * Blaster gamepads. - */ - -static void __init js_cr_init_corr(int mode, struct js_corr *corr) -{ - int i; - - switch (mode) { - - case JS_CR_MODE_BGPC: - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - } -} - -/* - * js_cr_probe() probes for Blaster gamepads. - */ - -static struct js_port __init *js_cr_probe(int io, struct js_port *port) -{ - struct js_cr_info info; - char *names[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Blaster GamePad Cobra" }; - char axes[] = { 0, 0, 0, 0, 0, 0, 0, 0, 2 }; - char buttons[] = { 0, 0, 0, 0, 0, 0, 0, 0, 12 }; - unsigned int data[2]; - int i, r; - - if (check_region(io, 1)) return port; - - info.mode[0] = info.mode[1] = 0; - - if (!(r = js_cr_read_packet(io, data))) - return port; - - for (i = 0; i < 2; i++) { - if (r & (1 << i)) { - if (~data[i] & 1) { - info.mode[i] = JS_CR_MODE_BGPC; - } else { - info.mode[i] = (data[i] >> 2) & 7; - } - if (!names[info.mode[i]]) { - printk(KERN_WARNING "joy-creative: Unknown Creative device %d at %#x\n", - info.mode[i], io); - info.mode[i] = 0; - } - } - } - - if (!info.mode[0] && !info.mode[1]) return port; - - info.io = io; - - request_region(io, 1, "joystick (creative)"); - port = js_register_port(port, &info, 2, sizeof(struct js_cr_info), js_cr_read); - - for (i = 0; i < 2; i++) - if (info.mode[i]) { - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], - names[info.mode[i]], THIS_MODULE, NULL, NULL), - names[info.mode[i]], io); - js_cr_init_corr(info.mode[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_cr_init(void) -#endif -{ - int *p; - - for (p = js_cr_port_list; *p; p++) js_cr_port = js_cr_probe(*p, js_cr_port); - if (js_cr_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-creative: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_cr_info *info; - - while (js_cr_port) { - for (i = 0; i < js_cr_port->ndevs; i++) - if (js_cr_port->devs[i]) - js_unregister_device(js_cr_port->devs[i]); - info = js_cr_port->info; - release_region(info->io, 1); - js_cr_port = js_unregister_port(js_cr_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-db9.c b/drivers/char/joystick/joy-db9.c deleted file mode 100644 index 41169b12c..000000000 --- a/drivers/char/joystick/joy-db9.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * joy-db9.c Version 0.6V - * - * Copyright (c) 1998 Andree Borrmann - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * console (Atari, Amstrad, Commodore, Amiga, Sega) joysticks - * and gamepads connected to the parallel port. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_db9, "2i"); -MODULE_PARM(js_db9_2, "2i"); -MODULE_PARM(js_db9_3, "2i"); - -#define JS_MULTI_STICK 0x01 -#define JS_MULTI2_STICK 0x02 -#define JS_GENESIS_PAD 0x03 -#define JS_GENESIS5_PAD 0x05 -#define JS_GENESIS6_PAD 0x06 -#define JS_SATURN_PAD 0x07 -#define JS_MULTI_0802 0x08 -#define JS_MULTI_0802_2 0x09 -#define JS_MAX_PAD 0x0A - -#define JS_DB9_UP 0x01 -#define JS_DB9_DOWN 0x02 -#define JS_DB9_LEFT 0x04 -#define JS_DB9_RIGHT 0x08 -#define JS_DB9_FIRE1 0x10 -#define JS_DB9_FIRE2 0x20 -#define JS_DB9_FIRE3 0x40 -#define JS_DB9_FIRE4 0x80 - -#define JS_DB9_NORMAL 0x2a -#define JS_DB9_NOSELECT 0x28 - -#define JS_DB9_SATURN0 0x20 -#define JS_DB9_SATURN1 0x22 -#define JS_DB9_SATURN2 0x24 -#define JS_DB9_SATURN3 0x26 - -#define JS_GENESIS6_DELAY 14 - -static struct js_port* js_db9_port = NULL; - -static int js_db9[] __initdata = { -1, 0 }; -static int js_db9_2[] __initdata = { -1, 0 }; -static int js_db9_3[] __initdata = { -1, 0 }; - -struct js_db9_info { - struct pardevice *port; /* parport device */ - int mode; /* pad mode */ -}; - -/* - * js_db9_read() reads and analyzes db9 joystick data. - */ - -static int js_db9_read(void *xinfo, int **axes, int **buttons) -{ - struct js_db9_info *info = xinfo; - int data; - - switch(info->mode) - { - case JS_MULTI_0802_2: - - data = JS_PAR_DATA_IN(info->port) >> 3; - - axes[1][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[1][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[1][0] = (data&JS_DB9_FIRE1?0:1); - - case JS_MULTI_0802: - - data = JS_PAR_STATUS(info->port) >> 3; - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?1:0); - - break; - - case JS_MULTI_STICK: - - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:1); - - break; - - case JS_MULTI2_STICK: - - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:2); - - break; - - case JS_GENESIS_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:2) | (data&JS_DB9_FIRE2?0:4); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:1) | (data&JS_DB9_FIRE2?0:8); - - break; - - case JS_GENESIS5_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08) | - (data&JS_DB9_LEFT ?0:0x10) | (data&JS_DB9_RIGHT?0:0x20); - break; - - case JS_GENESIS6_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT,info->port); /* 1 */ - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - buttons[0][0] = (data&JS_DB9_FIRE1?0:0x02) | (data&JS_DB9_FIRE2?0:0x04); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_FIRE1?0:0x01) | (data&JS_DB9_FIRE2?0:0x08); - - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 2 */ - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 3 */ - udelay(JS_GENESIS6_DELAY); - data=JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_LEFT?0:0x10) | (data&JS_DB9_DOWN ?0:0x20) | - (data&JS_DB9_UP ?0:0x40) | (data&JS_DB9_RIGHT?0:0x80); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NOSELECT, info->port); /* 4 */ - udelay(JS_GENESIS6_DELAY); - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - - break; - - case JS_SATURN_PAD: - - JS_PAR_CTRL_OUT(JS_DB9_SATURN0, info->port); - data = JS_PAR_DATA_IN(info->port); - - buttons[0][0] = (data&JS_DB9_UP ?0:0x20) | (data&JS_DB9_DOWN ?0:0x10) | - (data&JS_DB9_LEFT?0:0x08) | (data&JS_DB9_RIGHT?0:0x40); - - JS_PAR_CTRL_OUT(JS_DB9_SATURN2, info->port); - data = JS_PAR_DATA_IN(info->port); - - axes[0][1] = (data&JS_DB9_DOWN ?0:1) - (data&JS_DB9_UP ?0:1); - axes[0][0] = (data&JS_DB9_RIGHT?0:1) - (data&JS_DB9_LEFT?0:1); - - JS_PAR_CTRL_OUT(JS_DB9_NORMAL, info->port); - data = JS_PAR_DATA_IN(info->port); - - buttons[0][0] |= (data&JS_DB9_UP ?0:0x02) | (data&JS_DB9_DOWN ?0:0x04) | - (data&JS_DB9_LEFT?0:0x01) | (data&JS_DB9_RIGHT?0:0x80); - - - break; - - default: - return -1; - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: race possible. - */ - -int js_db9_open(struct js_dev *dev) -{ - struct js_db9_info *info = dev->port->info; - - if (!MOD_IN_USE) { - if (parport_claim(info->port)) return -EBUSY; - - JS_PAR_DATA_OUT(0xff, info->port); - if (info->mode != JS_MULTI_0802) - JS_PAR_ECTRL_OUT(0x35,info->port); /* enable PS/2 mode: */ - JS_PAR_CTRL_OUT(JS_DB9_NORMAL,info->port); /* reverse direction, enable Select signal */ - } - - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_db9_close(struct js_dev *dev) -{ - struct js_db9_info *info = dev->port->info; - - MOD_DEC_USE_COUNT; - - if (!MOD_IN_USE) { - - JS_PAR_CTRL_OUT(0x00,info->port); /* normal direction */ - if (info->mode != JS_MULTI_0802) - JS_PAR_ECTRL_OUT(0x15,info->port); /* enable normal mode */ - - parport_release(info->port); - } - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_db9_info *info; - int i; - - while (js_db9_port) { - info = js_db9_port->info; - - for (i = 0; i < js_db9_port->ndevs; i++) - if (js_db9_port->devs[i]) - js_unregister_device(js_db9_port->devs[i]); - parport_unregister_device(info->port); - js_db9_port = js_unregister_port(js_db9_port); - } - -} -#endif - -/* - * js_db9_init_corr() initializes correction values of - * db9 gamepads. - */ - -static void __init js_db9_init_corr(struct js_corr *corr) -{ - int i; - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } -} - -/* - * js_db9_probe() probes for db9 gamepads. - */ - -static struct js_port __init *js_db9_probe(int *config, struct js_port *port) -{ - struct js_db9_info info; - struct parport *pp; - int i; - char buttons[JS_MAX_PAD] = {0,1,2,4,0,6,8,8,1,1}; - char *name[JS_MAX_PAD] = {NULL, "Multisystem joystick", "Multisystem joystick (2 fire)", "Genesis pad", - NULL, "Genesis 5 pad", "Genesis 6 pad", "Saturn pad", "Multisystem (0.8.0.2) joystick", - "Multisystem (0.8.0.2-dual) joystick"}; - - if (config[0] < 0) return port; - if (config[1] < 0 || config[1] >= JS_MAX_PAD || !name[config[1]]) return port; - - info.mode = config[1]; - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-db9: no such parport\n"); - return port; - } - - if (!(pp->modes & (PARPORT_MODE_PCPS2 | PARPORT_MODE_PCECPPS2)) && info.mode != JS_MULTI_0802) { - printk(KERN_ERR "js-db9: specified parport is not bidirectional\n"); - return port; - } - - info.port = parport_register_device(pp, "joystick (db9)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info.port) - return port; - - port = js_register_port(port, &info, 1 + (info.mode == JS_MULTI_0802_2), sizeof(struct js_db9_info), js_db9_read); - - for (i = 0; i < 1 + (info.mode == JS_MULTI_0802_2); i++) { - printk(KERN_INFO "js%d: %s on %s\n", - js_register_device(port, i, 2, buttons[info.mode], name[info.mode], NULL, js_db9_open, js_db9_close), - name[info.mode], info.port->port->name); - - js_db9_init_corr(port->corr[i]); - } - - - return port; -} - -#ifndef MODULE -int __init js_db9_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9[i] = ints[i+1]; - return 1; -} -int __init js_db9_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_2[i] = ints[i+1]; - return 1; -} -int __init js_db9_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_db9_3[i] = ints[i+1]; - return 1; -} -__setup("js_db9=", js_db9_setup); -__setup("js_db9_2=", js_db9_setup_2); -__setup("js_db9_3=", js_db9_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_db9_init(void) -#endif -{ - js_db9_port = js_db9_probe(js_db9, js_db9_port); - js_db9_port = js_db9_probe(js_db9_2, js_db9_port); - js_db9_port = js_db9_probe(js_db9_3, js_db9_port); - - if (js_db9_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-db9: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-gravis.c b/drivers/char/joystick/joy-gravis.c deleted file mode 100644 index 84a6def16..000000000 --- a/drivers/char/joystick/joy-gravis.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * joy-gravis.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Gravis GrIP digital joystick family. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_GR_MODE_GPP 1 -#define JS_GR_LENGTH_GPP 24 -#define JS_GR_STROBE_GPP 400 - -#define JS_GR_MODE_XT 2 -#define JS_GR_MODE_BD 3 -#define JS_GR_LENGTH_XT 4 -#define JS_GR_STROBE_XT 200 -#define JS_GR_MAX_CHUNKS_XT 10 -#define JS_GR_MAX_BITS_XT 30 - -static int js_gr_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_gr_port __initdata = NULL; - -struct js_gr_info { - int io; - unsigned char mode[2]; -}; - -/* - * js_gr_gpp_read_packet() reads a Gravis GamePad Pro packet. - */ - -static int js_gr_gpp_read_packet(int io, int shift, unsigned int *data) -{ - unsigned long flags; - unsigned char u, v; - unsigned int t, p; - int i; - - i = 0; - data[0] = 0; - p = t = JS_GR_STROBE_GPP; - p += JS_GR_STROBE_GPP; - - __save_flags(flags); - __cli(); - - v = inb(io) >> shift; - - do { - t--; - u = v; v = (inb(io) >> shift) & 3; - if (~v & u & 1) { - data[0] |= (v >> 1) << i++; - p = t = (p - t) << 1; - } - } while (i < JS_GR_LENGTH_GPP && t > 0); - - __restore_flags(flags); - - if (i < JS_GR_LENGTH_GPP) return -1; - - for (i = 0; i < JS_GR_LENGTH_GPP && (data[0] & 0xfe4210) ^ 0x7c0000; i++) - data[0] = data[0] >> 1 | (data[0] & 1) << (JS_GR_LENGTH_GPP - 1); - - return -(i == JS_GR_LENGTH_GPP); -} - -/* - * js_gr_xt_read_packet() reads a Gravis Xterminator packet. - */ - -static int js_gr_xt_read_packet(int io, int shift, unsigned int *data) -{ - unsigned int i, j, buf, crc; - unsigned char u, v, w; - unsigned long flags; - unsigned int t, p; - char status; - - data[0] = data[1] = data[2] = data[3] = 0; - status = buf = i = j = 0; - p = t = JS_GR_STROBE_XT; - p += JS_GR_STROBE_XT; - - __save_flags(flags); - __cli(); - - v = w = (inb(io) >> shift) & 3; - - do { - t--; - u = (inb(io) >> shift) & 3; - - if (u ^ v) { - - if ((u ^ v) & 1) { - p = t = (p - t) << 2; - buf = (buf << 1) | (u >> 1); - i++; - } else - - if ((((u ^ v) & (v ^ w)) >> 1) & ~(u | v | w) & 1) { - p = t = (p - t) << 2; - if (i == 20) { - crc = buf ^ (buf >> 7) ^ (buf >> 14); - if (!((crc ^ (0x25cb9e70 >> ((crc >> 2) & 0x1c))) & 0xf)) { - data[buf >> 18] = buf >> 4; - status |= 1 << (buf >> 18); - } - j++; - } - buf = 0; - i = 0; - } - - w = v; - v = u; - } - - } while (status != 0xf && i < JS_GR_MAX_BITS_XT && j < JS_GR_MAX_CHUNKS_XT && t > 0); - - __restore_flags(flags); - - return -(status != 0xf); -} - -/* - * js_gr_read() reads and analyzes GrIP joystick data. - */ - -static int js_gr_read(void *xinfo, int **axes, int **buttons) -{ - struct js_gr_info *info = xinfo; - unsigned int data[JS_GR_LENGTH_XT]; - int i; - - for (i = 0; i < 2; i++) - switch (info->mode[i]) { - - case JS_GR_MODE_GPP: - - if (js_gr_gpp_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = ((data[0] >> 15) & 1) - ((data[0] >> 16) & 1); - axes[i][1] = ((data[0] >> 13) & 1) - ((data[0] >> 12) & 1); - - data[0] = ((data[0] >> 6) & 0x37) | (data[0] & 0x08) | ((data[0] << 1) & 0x40) | - ((data[0] << 5) & 0x80) | ((data[0] << 8) & 0x300); - - buttons[i][0] = (data[0] & 0xfc) | ((data[0] >> 1) & 0x101) | ((data[0] << 1) & 0x202); - - break; - - case JS_GR_MODE_XT: - - if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = (data[0] >> 2) & 0x3f; - axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); - axes[i][2] = (data[1] >> 2) & 0x3f; - axes[i][3] = (data[1] >> 8) & 0x3f; - axes[i][4] = (data[2] >> 8) & 0x3f; - - axes[i][5] = ((data[2] >> 1) & 1) - ( data[2] & 1); - axes[i][6] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); - axes[i][7] = ((data[2] >> 5) & 1) - ((data[2] >> 4) & 1); - axes[i][8] = ((data[2] >> 6) & 1) - ((data[2] >> 7) & 1); - - buttons[i][0] = (data[3] >> 3) & 0x7ff; - - break; - - case JS_GR_MODE_BD: - - if (js_gr_xt_read_packet(info->io, (i << 1) + 4, data)) return -1; - - axes[i][0] = (data[0] >> 2) & 0x3f; - axes[i][1] = 63 - ((data[0] >> 8) & 0x3f); - axes[i][2] = (data[2] >> 8) & 0x3f; - - axes[i][3] = ((data[2] >> 1) & 1) - ( data[2] & 1); - axes[i][4] = ((data[2] >> 2) & 1) - ((data[2] >> 3) & 1); - - buttons[i][0] = ((data[3] >> 6) & 0x01) | ((data[3] >> 3) & 0x06) - | ((data[3] >> 4) & 0x18); - - break; - - default: - break; - - } - - - return 0; -} - -/* - * js_gr_init_corr() initializes correction values of - * GrIP joysticks. - */ - -static void __init js_gr_init_corr(int mode, struct js_corr *corr) -{ - int i; - - switch (mode) { - - case JS_GR_MODE_GPP: - - for (i = 0; i < 2; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - case JS_GR_MODE_XT: - - for (i = 0; i < 5; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 31 - 4; - corr[i].coef[1] = 32 + 4; - corr[i].coef[2] = (1 << 29) / (32 - 14); - corr[i].coef[3] = (1 << 29) / (32 - 14); - } - - for (i = 5; i < 9; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - case JS_GR_MODE_BD: - - for (i = 0; i < 3; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 31 - 4; - corr[i].coef[1] = 32 + 4; - corr[i].coef[2] = (1 << 29) / (32 - 14); - corr[i].coef[3] = (1 << 29) / (32 - 14); - } - - for (i = 3; i < 5; i++) { - corr[i].type = JS_CORR_BROKEN; - corr[i].prec = 0; - corr[i].coef[0] = 0; - corr[i].coef[1] = 0; - corr[i].coef[2] = (1 << 29); - corr[i].coef[3] = (1 << 29); - } - - break; - - } -} - -/* - * js_gr_probe() probes for GrIP joysticks. - */ - -static struct js_port __init *js_gr_probe(int io, struct js_port *port) -{ - struct js_gr_info info; - char *names[] = { NULL, "Gravis GamePad Pro", "Gravis Xterminator", "Gravis Blackhawk Digital"}; - char axes[] = { 0, 2, 9, 5}; - char buttons[] = { 0, 10, 11, 5}; - unsigned int data[JS_GR_LENGTH_XT]; - int i; - - if (check_region(io, 1)) return port; - - info.mode[0] = info.mode[1] = 0; - - for (i = 0; i < 2; i++) { - if (!js_gr_gpp_read_packet(io, (i << 1) + 4, data)) info.mode[i] = JS_GR_MODE_GPP; - if (!js_gr_xt_read_packet(io, (i << 1) + 4, data)) { - if ((data[3] & 7) == 7) - info.mode[i] = JS_GR_MODE_XT; - if ((data[3] & 7) == 0) - info.mode[i] = JS_GR_MODE_BD; - } - } - - if (!info.mode[0] && !info.mode[1]) return port; - - info.io = io; - - request_region(io, 1, "joystick (gravis)"); - port = js_register_port(port, &info, 2, sizeof(struct js_gr_info), js_gr_read); - - for (i = 0; i < 2; i++) - if (info.mode[i]) { - printk(KERN_INFO "js%d: %s at %#x\n", - js_register_device(port, i, axes[info.mode[i]], buttons[info.mode[i]], - names[info.mode[i]], THIS_MODULE, NULL, NULL), - names[info.mode[i]], io); - js_gr_init_corr(info.mode[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_gr_init(void) -#endif -{ - int *p; - - for (p = js_gr_port_list; *p; p++) js_gr_port = js_gr_probe(*p, js_gr_port); - if (js_gr_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-gravis: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_gr_info *info; - - while (js_gr_port) { - for (i = 0; i < js_gr_port->ndevs; i++) - if (js_gr_port->devs[i]) - js_unregister_device(js_gr_port->devs[i]); - info = js_gr_port->info; - release_region(info->io, 1); - js_gr_port = js_unregister_port(js_gr_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-lightning.c b/drivers/char/joystick/joy-lightning.c deleted file mode 100644 index 26c18856a..000000000 --- a/drivers/char/joystick/joy-lightning.c +++ /dev/null @@ -1,351 +0,0 @@ -/* - * joy-lightning.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * PDPI Lightning 4 gamecards and analog joysticks connected - * to them. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_L4_PORT 0x201 -#define JS_L4_SELECT_ANALOG 0xa4 -#define JS_L4_SELECT_DIGITAL 0xa5 -#define JS_L4_SELECT_SECONDARY 0xa6 -#define JS_L4_CMD_ID 0x80 -#define JS_L4_CMD_GETCAL 0x92 -#define JS_L4_CMD_SETCAL 0x93 -#define JS_L4_ID 0x04 -#define JS_L4_BUSY 0x01 -#define JS_L4_TIMEOUT 80 /* 80 us */ - -static struct js_port* __initdata js_l4_port = NULL; - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_l4, "2-24i"); - -static int __initdata js_l4[] = { -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0 }; - -#include "joy-analog.h" - -struct js_l4_info { - int port; - struct js_an_info an; -}; - -/* - * js_l4_wait_ready() waits for the L4 to become ready. - */ - -static int js_l4_wait_ready(void) -{ - unsigned int t; - t = JS_L4_TIMEOUT; - while ((inb(JS_L4_PORT) & JS_L4_BUSY) && t > 0) t--; - return -(t<=0); -} - -/* - * js_l4_read() reads data from the Lightning 4. - */ - -static int js_l4_read(void *xinfo, int **axes, int **buttons) -{ - struct js_l4_info *info = xinfo; - int i; - unsigned char status; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (info->port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(info->port & 3, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - status = inb(JS_L4_PORT); - - for (i = 0; i < 4; i++) - if (status & (1 << i)) { - if (js_l4_wait_ready()) return -1; - info->an.axes[i] = inb(JS_L4_PORT); - } - - if (status & 0x10) { - if (js_l4_wait_ready()) return -1; - info->an.buttons = inb(JS_L4_PORT); - } - - js_an_decode(&info->an, axes, buttons); - - return 0; -} - -/* - * js_l4_getcal() reads the L4 with calibration values. - */ - -static int js_l4_getcal(int port, int *cal) -{ - int i; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(JS_L4_CMD_GETCAL, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; - - if (js_l4_wait_ready()) return -1; - outb(port & 3, JS_L4_PORT); - - for (i = 0; i < 4; i++) { - if (js_l4_wait_ready()) return -1; - cal[i] = inb(JS_L4_PORT); - } - - return 0; -} - -/* - * js_l4_setcal() programs the L4 with calibration values. - */ - -static int js_l4_setcal(int port, int *cal) -{ - int i; - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + (port >> 2), JS_L4_PORT); - - if (inb(JS_L4_PORT) & JS_L4_BUSY) return -1; - outb(JS_L4_CMD_SETCAL, JS_L4_PORT); - - if (js_l4_wait_ready()) return -1; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + (port >> 2)) return -1; - - if (js_l4_wait_ready()) return -1; - outb(port & 3, JS_L4_PORT); - - for (i = 0; i < 4; i++) { - if (js_l4_wait_ready()) return -1; - outb(cal[i], JS_L4_PORT); - } - - return 0; -} - -/* - * js_l4_calibrate() calibrates the L4 for the attached device, so - * that the device's resistance fits into the L4's 8-bit range. - */ - -static void js_l4_calibrate(struct js_l4_info *info) -{ - int i; - int cal[4]; - int axes[4]; - int t; - - js_l4_getcal(info->port, cal); - - for (i = 0; i < 4; i++) - axes[i] = info->an.axes[i]; - - if ((info->an.extensions & JS_AN_BUTTON_PXY_X) && !(info->an.extensions & JS_AN_BUTTON_PXY_U)) - axes[2] >>= 1; /* Pad button X */ - - if ((info->an.extensions & JS_AN_BUTTON_PXY_Y) && !(info->an.extensions & JS_AN_BUTTON_PXY_V)) - axes[3] >>= 1; /* Pad button Y */ - - if (info->an.extensions & JS_AN_HAT_FCS) - axes[3] >>= 1; /* FCS hat */ - - if (((info->an.mask[0] & 0xb) == 0xb) || ((info->an.mask[1] & 0xb) == 0xb)) - axes[3] = (axes[0] + axes[1]) >> 1; /* Throttle */ - - for (i = 0; i < 4; i++) { - t = (axes[i] * cal[i]) / 100; - if (t > 255) t = 255; - info->an.axes[i] = (info->an.axes[i] * cal[i]) / t; - cal[i] = t; - } - - js_l4_setcal(info->port, cal); -} - -/* - * js_l4_probe() probes for joysticks on the L4 cards. - */ - -static struct js_port __init *js_l4_probe(unsigned char *cards, int l4port, int mask0, int mask1, struct js_port *port) -{ - struct js_l4_info iniinfo; - struct js_l4_info *info = &iniinfo; - int cal[4] = {255,255,255,255}; - int i, numdev; - unsigned char u; - - if (l4port < 0) return port; - if (!cards[(l4port >> 2)]) return port; - - memset(info, 0, sizeof(struct js_l4_info)); - info->port = l4port; - - if (cards[l4port >> 2] > 0x28) js_l4_setcal(info->port, cal); - if (js_l4_read(info, NULL, NULL)) return port; - - for (i = u = 0; i < 4; i++) if (info->an.axes[i] < 253) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - port = js_register_port(port, info, numdev, sizeof(struct js_l4_info), js_l4_read); - - info = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_INFO "js%d: %s on L4 port %d\n", - js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), - js_an_name(i, &info->an), info->port); - - js_l4_calibrate(info); - js_l4_read(info, port->axes, port->buttons); - js_an_init_corr(&info->an, port->axes, port->corr, 0); - - return port; -} - -/* - * js_l4_card_probe() probes for presence of the L4 card(s). - */ - -static void __init js_l4_card_probe(unsigned char *cards) -{ - int i; - unsigned char rev = 0; - - if (check_region(JS_L4_PORT, 1)) return; - - for (i = 0; i < 2; i++) { - - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - outb(JS_L4_SELECT_DIGITAL + i, JS_L4_PORT); /* Select card 0-1 */ - - if (inb(JS_L4_PORT) & JS_L4_BUSY) continue; - outb(JS_L4_CMD_ID, JS_L4_PORT); /* Get card ID & rev */ - - if (js_l4_wait_ready()) continue; - if (inb(JS_L4_PORT) != JS_L4_SELECT_DIGITAL + i) continue; - - if (js_l4_wait_ready()) continue; - if (inb(JS_L4_PORT) != JS_L4_ID) continue; - - if (js_l4_wait_ready()) continue; - rev = inb(JS_L4_PORT); - - cards[i] = rev; - - printk(KERN_INFO "js: PDPI Lightning 4 %s card (ports %d-%d) firmware v%d.%d at %#x\n", - i ? "secondary" : "primary", (i << 2), (i << 2) + 3, rev >> 4, rev & 0xf, JS_L4_PORT); - } - -} - -#ifndef MODULE -int __init js_l4_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(24); - for (i = 0; i <= ints[0] && i < 24; i++) js_l4[i] = ints[i+1]; - return 1; -} -__setup("js_l4=", js_l4_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_l4_init(void) -#endif -{ - int i; - unsigned char cards[2] = {0, 0}; - - js_l4_card_probe(cards); - - if (js_l4[0] >= 0) { - for (i = 0; (js_l4[i*3] >= 0) && i < 8; i++) - js_l4_port = js_l4_probe(cards, js_l4[i*3], js_l4[i*3+1], js_l4[i*3+2], js_l4_port); - } else { - for (i = 0; i < 8; i++) - js_l4_port = js_l4_probe(cards, i, 0, 0, js_l4_port); - } - - if (!js_l4_port) { -#ifdef MODULE - printk(KERN_WARNING "joy-lightning: no joysticks found\n"); -#endif - return -ENODEV; - } - - request_region(JS_L4_PORT, 1, "joystick (lightning)"); - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - int cal[4] = {59, 59, 59, 59}; - struct js_l4_info *info; - - while (js_l4_port) { - for (i = 0; i < js_l4_port->ndevs; i++) - if (js_l4_port->devs[i]) - js_unregister_device(js_l4_port->devs[i]); - info = js_l4_port->info; - js_l4_setcal(info->port, cal); - js_l4_port = js_unregister_port(js_l4_port); - } - outb(JS_L4_SELECT_ANALOG, JS_L4_PORT); - release_region(JS_L4_PORT, 1); -} -#endif diff --git a/drivers/char/joystick/joy-logitech.c b/drivers/char/joystick/joy-logitech.c deleted file mode 100644 index 6044cbfb0..000000000 --- a/drivers/char/joystick/joy-logitech.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * joy-logitech.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Logitech ADI joystick family. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/malloc.h> -#include <linux/init.h> - -/* - * Times array sizes, flags, ids. - */ - -#undef JS_LT_DEBUG - -#define JS_LT_MAX_START 400 /* Trigger to packet timeout [400us] */ - -#define JS_LT_MAX_LENGTH 256 -#define JS_LT_MIN_LENGTH 8 -#define JS_LT_MIN_LEN_LENGTH 10 -#define JS_LT_MIN_ID_LENGTH 66 -#define JS_LT_MAX_NAME_LENGTH 16 - -#define JS_LT_INIT_DELAY 10 /* Delay after init packet [10ms] */ -#define JS_LT_DATA_DELAY 4 /* Delay after data packet [4ms] */ - -#define JS_LT_FLAG_HAT 0x04 -#define JS_LT_FLAG_10BIT 0x08 - -#define JS_LT_ID_WMED 0x00 -#define JS_LT_ID_TPD 0x01 -#define JS_LT_ID_WMI 0x04 -#define JS_LT_ID_WGP 0x06 -#define JS_LT_ID_WM3D 0x07 -#define JS_LT_ID_WGPE 0x08 - -#define JS_LT_BUG_BUTTONS 0x01 -#define JS_LT_BUG_LONGID 0x02 -#define JS_LT_BUG_LONGDATA 0x04 -#define JS_LT_BUG_IGNTRIG 0x08 - -/* - * Port probing variables. - */ - -static int js_lt_port_list[] __initdata = { 0x201, 0 }; -static struct js_port* js_lt_port __initdata = NULL; - -/* - * Device names. - */ - -#define JS_LT_MAX_ID 10 - -static char *js_lt_names[] = { "WingMan Extreme Digital", "ThunderPad Digital", "SideCar", "CyberMan 2", - "WingMan Interceptor", "WingMan Formula", "WingMan GamePad", - "WingMan Extreme Digital 3D", "WingMan GamePad Extreme", - "WingMan GamePad USB", "Unknown Device %#x"}; - -/* - * Hat to axis conversion arrays. - */ - -static struct { - int x; - int y; -} js_lt_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -/* - * Per-port information. - */ - -struct js_lt_info { - int io; - int length[2]; - int ret[2]; - int idx[2]; - unsigned char id[2]; - char buttons[2]; - char axes10[2]; - char axes8[2]; - char pad[2]; - char hats[2]; - char name[2][JS_LT_MAX_NAME_LENGTH]; - unsigned char data[2][JS_LT_MAX_LENGTH]; - char bugs[2]; -}; - -/* - * js_lt_read_packet() reads a Logitech ADI packet. - */ - -static void js_lt_read_packet(struct js_lt_info *info) -{ - unsigned char u, v, w, x, z; - int t[2], s[2], p[2], i; - unsigned long flags; - - for (i = 0; i < 2; i++) { - info->ret[i] = -1; - p[i] = t[i] = JS_LT_MAX_START; - s[i] = 0; - } - - __save_flags(flags); - __cli(); - - outb(0xff, info->io); - v = z = inb(info->io); - - do { - u = v; - w = u ^ (v = x = inb(info->io)); - for (i = 0; i < 2; i++, w >>= 2, x >>= 2) { - t[i]--; - if ((w & 0x30) && s[i]) { - if ((w & 0x30) < 0x30 && info->ret[i] < JS_LT_MAX_LENGTH && t[i] > 0) { - info->data[i][++info->ret[i]] = w; - p[i] = t[i] = (p[i] - t[i]) << 1; - } else t[i] = 0; - } else if (!(x & 0x30)) s[i] = 1; - } - } while (t[0] > 0 || t[1] > 0); - - __restore_flags(flags); - - for (i = 0; i < 2; i++, z >>= 2) - if ((z & 0x30) && info->ret[i] > 0) - info->bugs[i] |= JS_LT_BUG_BUTTONS; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: read %d %d bits\n", info->ret[0], info->ret[1]); - printk(KERN_DEBUG "joy-logitech: stream0:"); - for (i = 0; i <= info->ret[0]; i++) printk("%d", (info->data[0][i] >> 5) & 1); - printk("\n"); - printk(KERN_DEBUG "joy-logitech: stream1:"); - for (i = 0; i <= info->ret[1]; i++) printk("%d", (info->data[1][i] >> 5) & 1); - printk("\n"); -#endif - - return; -} - -/* - * js_lt_move_bits() detects a possible 2-stream mode, and moves - * the bits accordingly. - */ - -static void js_lt_move_bits(struct js_lt_info *info, int length) -{ - int i; - - info->idx[0] = info->idx[1] = 0; - - if (info->ret[0] <= 0 || info->ret[1] <= 0) return; - if (info->data[0][0] & 0x20 || ~info->data[1][0] & 0x20) return; - - for (i = 1; i <= info->ret[1]; i++) - info->data[0][((length - 1) >> 1) + i + 1] = info->data[1][i]; - - info->ret[0] += info->ret[1]; - info->ret[1] = -1; -} - -/* - * js_lt_get_bits() gathers bits from the data packet. - */ - -static inline int js_lt_get_bits(struct js_lt_info *info, int device, int count) -{ - int bits = 0; - int i; - if ((info->idx[device] += count) > info->ret[device]) return 0; - for (i = 0; i < count; i++) bits |= ((info->data[device][info->idx[device] - i] >> 5) & 1) << i; - return bits; -} - -/* - * js_lt_read() reads and analyzes Logitech joystick data. - */ - -static int js_lt_read(void *xinfo, int **axes, int **buttons) -{ - struct js_lt_info *info = xinfo; - int i, j, k, l, t; - int ret = 0; - - js_lt_read_packet(info); - js_lt_move_bits(info, info->length[0]); - - for (i = 0; i < 2; i++) { - - if (!info->length[i]) continue; - - if (info->length[i] > info->ret[i] || - info->id[i] != (js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4))) { - ret = -1; - continue; - } - - if (info->length[i] < info->ret[i]) - info->bugs[i] |= JS_LT_BUG_LONGDATA; - - k = l = 0; - - for (j = 0; j < info->axes10[i]; j++) - axes[i][k++] = js_lt_get_bits(info, i, 10); - - for (j = 0; j < info->axes8[i]; j++) - axes[i][k++] = js_lt_get_bits(info, i, 8); - - for (j = 0; j <= (info->buttons[i] - 1) >> 5; j++) buttons[i][j] = 0; - - for (j = 0; j < info->buttons[i] && j < 63; j++) { - if (j == info->pad[i]) { - t = js_lt_get_bits(info, i, 4); - axes[i][k++] = ((t >> 2) & 1) - ( t & 1); - axes[i][k++] = ((t >> 1) & 1) - ((t >> 3) & 1); - } - buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); - l++; - } - - for (j = 0; j < info->hats[i]; j++) { - if((t = js_lt_get_bits(info, i, 4)) > 8) { - if (t != 15) ret = -1; /* Hat press */ - t = 0; - } - axes[i][k++] = js_lt_hat_to_axis[t].x; - axes[i][k++] = js_lt_hat_to_axis[t].y; - } - - for (j = 63; j < info->buttons[i]; j++) { - buttons[i][l >> 5] |= js_lt_get_bits(info, i, 1) << (l & 0x1f); - l++; - } - } - - return ret; -} - -/* - * js_lt_init_digital() sends a trigger & delay sequence - * to reset and initialize a Logitech joystick into digital mode. - */ - -static void __init js_lt_init_digital(int io) -{ - int seq[] = { 3, 2, 3, 10, 6, 11, 7, 9, 11, 0 }; - int i; - - for (i = 0; seq[i]; i++) { - outb(0xff,io); - mdelay(seq[i]); - } -} - -/* - * js_lt_init_corr() initializes the correction values for - * Logitech joysticks. - */ - -static void __init js_lt_init_corr(int id, int naxes10, int naxes8, int naxes1, int *axes, struct js_corr *corr) -{ - int j; - - if (id == JS_LT_ID_WMED) axes[2] = 128; /* Throttle fixup */ - if (id == JS_LT_ID_WMI) axes[2] = 512; - if (id == JS_LT_ID_WM3D) axes[3] = 128; - - if (id == JS_LT_ID_WGPE) { /* Tilt fixup */ - axes[0] = 512; - axes[1] = 512; - } - - for (j = 0; j < naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 2; - corr[j].coef[0] = axes[j] - 16; - corr[j].coef[1] = axes[j] + 16; - corr[j].coef[2] = (1 << 29) / (256 - 32); - corr[j].coef[3] = (1 << 29) / (256 - 32); - } - - for (; j < naxes8 + naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 1; - corr[j].coef[0] = axes[j] - 2; - corr[j].coef[1] = axes[j] + 2; - corr[j].coef[2] = (1 << 29) / (64 - 16); - corr[j].coef[3] = (1 << 29) / (64 - 16); - } - - for (; j < naxes1 + naxes8 + naxes10; j++) { - corr[j].type = JS_CORR_BROKEN; - corr[j].prec = 0; - corr[j].coef[0] = 0; - corr[j].coef[1] = 0; - corr[j].coef[2] = (1 << 29); - corr[j].coef[3] = (1 << 29); - } -} - -/* - * js_lt_probe() probes for Logitech type joysticks. - */ - -static struct js_port __init *js_lt_probe(int io, struct js_port *port) -{ - struct js_lt_info iniinfo; - struct js_lt_info *info = &iniinfo; - char name[32]; - int i, j, t; - - if (check_region(io, 1)) return port; - - js_lt_init_digital(io); - - memset(info, 0, sizeof(struct js_lt_info)); - - info->length[0] = info->length[1] = JS_LT_MAX_LENGTH; - - info->io = io; - js_lt_read_packet(info); - - if (info->ret[0] >= JS_LT_MIN_LEN_LENGTH) - js_lt_move_bits(info, js_lt_get_bits(info, 0, 10)); - - info->length[0] = info->length[1] = 0; - - for (i = 0; i < 2; i++) { - - if (info->ret[i] < JS_LT_MIN_ID_LENGTH) continue; /* Minimum ID packet length */ - - if (info->ret[i] < (t = js_lt_get_bits(info, i, 10))) { - printk(KERN_WARNING "joy-logitech: Short ID packet: reported: %d != read: %d\n", - t, info->ret[i]); - continue; - } - - if (info->ret[i] > t) - info->bugs[i] |= JS_LT_BUG_LONGID; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: id %d length %d", i, t); -#endif - - info->id[i] = js_lt_get_bits(info, i, 4) | (js_lt_get_bits(info, i, 4) << 4); - - if ((t = js_lt_get_bits(info, i, 4)) & JS_LT_FLAG_HAT) info->hats[i]++; - -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "joy-logitech: id %d flags %d", i, t); -#endif - - if ((info->length[i] = js_lt_get_bits(info, i, 10)) >= JS_LT_MAX_LENGTH) { - printk(KERN_WARNING "joy-logitech: Expected packet length too long (%d).\n", - info->length[i]); - continue; - } - - if (info->length[i] < JS_LT_MIN_LENGTH) { - printk(KERN_WARNING "joy-logitech: Expected packet length too short (%d).\n", - info->length[i]); - continue; - } - - info->axes8[i] = js_lt_get_bits(info, i, 4); - info->buttons[i] = js_lt_get_bits(info, i, 6); - - if (js_lt_get_bits(info, i, 6) != 8 && info->hats[i]) { - printk(KERN_WARNING "joy-logitech: Other than 8-dir POVs not supported yet.\n"); - continue; - } - - info->buttons[i] += js_lt_get_bits(info, i, 6); - info->hats[i] += js_lt_get_bits(info, i, 4); - - j = js_lt_get_bits(info, i, 4); - - if (t & JS_LT_FLAG_10BIT) { - info->axes10[i] = info->axes8[i] - j; - info->axes8[i] = j; - } - - t = js_lt_get_bits(info, i, 4); - - for (j = 0; j < t; j++) - info->name[i][j] = js_lt_get_bits(info, i, 8); - info->name[i][j] = 0; - - switch (info->id[i]) { - case JS_LT_ID_TPD: - info->pad[i] = 4; - info->buttons[i] -= 4; - break; - case JS_LT_ID_WGP: - info->pad[i] = 0; - info->buttons[i] -= 4; - break; - default: - info->pad[i] = -1; - break; - } - - if (info->length[i] != - (t = 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + - info->hats[i] * 4 + (info->pad[i] != -1) * 4)) { - printk(KERN_WARNING "js%d: Expected lenght %d != data length %d\n", i, t, info->length[i]); - } - - } - - if (!info->length[0] && !info->length[1]) - return port; - - request_region(io, 1, "joystick (logitech)"); - - port = js_register_port(port, info, 2, sizeof(struct js_lt_info), js_lt_read); - info = port->info; - - for (i = 0; i < 2; i++) - if (info->length[i] > 0) { - sprintf(name, info->id[i] < JS_LT_MAX_ID ? - js_lt_names[info->id[i]] : js_lt_names[JS_LT_MAX_ID], info->id[i]); - printk(KERN_INFO "js%d: %s [%s] at %#x\n", - js_register_device(port, i, - info->axes10[i] + info->axes8[i] + ((info->hats[i] + (info->pad[i] >= 0)) << 1), - info->buttons[i], name, THIS_MODULE, NULL, NULL), name, info->name[i], io); - } - - mdelay(JS_LT_INIT_DELAY); - if (js_lt_read(info, port->axes, port->buttons)) { - if (info->ret[0] < 1) info->bugs[0] |= JS_LT_BUG_IGNTRIG; - if (info->ret[1] < 1) info->bugs[1] |= JS_LT_BUG_IGNTRIG; - mdelay(JS_LT_DATA_DELAY); - js_lt_read(info, port->axes, port->buttons); - } - - for (i = 0; i < 2; i++) - if (info->length[i] > 0) { -#ifdef JS_LT_DEBUG - printk(KERN_DEBUG "js%d: length %d ret %d id %d buttons %d axes10 %d axes8 %d " - "pad %d hats %d name %s explen %d\n", - i, info->length[i], info->ret[i], info->id[i], - info->buttons[i], info->axes10[i], info->axes8[i], - info->pad[i], info->hats[i], info->name[i], - 8 + info->buttons[i] + info->axes10[i] * 10 + info->axes8[i] * 8 + - info->hats[i] * 4 + (info->pad[i] != -1) * 4); -#endif - if (info->bugs[i]) { - printk(KERN_WARNING "js%d: Firmware bugs detected:%s%s%s%s\n", i, - info->bugs[i] & JS_LT_BUG_BUTTONS ? " init_buttons" : "", - info->bugs[i] & JS_LT_BUG_LONGID ? " long_id" : "", - info->bugs[i] & JS_LT_BUG_LONGDATA ? " long_data" : "", - info->bugs[i] & JS_LT_BUG_IGNTRIG ? " ignore_trigger" : ""); - } - js_lt_init_corr(info->id[i], info->axes10[i], info->axes8[i], - ((info->pad[i] >= 0) + info->hats[i]) << 1, port->axes[i], port->corr[i]); - } - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_lt_init(void) -#endif -{ - int *p; - - for (p = js_lt_port_list; *p; p++) js_lt_port = js_lt_probe(*p, js_lt_port); - if (js_lt_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-logitech: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_lt_info *info; - - while (js_lt_port) { - for (i = 0; i < js_lt_port->ndevs; i++) - if (js_lt_port->devs[i]) - js_unregister_device(js_lt_port->devs[i]); - info = js_lt_port->info; - release_region(info->io, 1); - js_lt_port = js_unregister_port(js_lt_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-magellan.c b/drivers/char/joystick/joy-magellan.c deleted file mode 100644 index cb14646e8..000000000 --- a/drivers/char/joystick/joy-magellan.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * joy-magellan.c Version 0.1 - * - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the Magellan and Space Mouse 6dof controllers. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_MAG 14 -#define JS_MAG_MAX_LENGTH 64 - -/* - * List of Magellans. - */ - -static struct js_port* js_mag_port = NULL; - -/* - * Per-Magellan data. - */ - -struct js_mag_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_MAG_MAX_LENGTH]; - unsigned char name[JS_MAG_MAX_LENGTH]; - char ack; - char used; -}; - -/* - * js_mag_crunch_nibbles() verifies that the bytes sent from the Magellan - * have correct upper nibbles for the lower ones, if not, the packet will - * be thrown away. It also strips these upper halves to simplify further - * processing. - */ - -static int js_mag_crunch_nibbles(unsigned char *data, int count) -{ - static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; - - do { - if (data[count] == nibbles[data[count] & 0xf]) - data[count] = data[count] & 0xf; - else - return -1; - } while (--count); - - return 0; -} - -/* - * js_mag_process_packet() decodes packets the driver receives from the - * Magellan. It updates the data accordingly, and sets an ACK flag - * to the type of last packet received, if received OK. - */ - -static void js_mag_process_packet(struct js_mag_info* info) -{ - int i; - - if (!info->idx) return; - - switch (info->data[0]) { - - case 'd': /* Axis data */ - if (info->idx != 25) return; - if (js_mag_crunch_nibbles(info->data, 24)) return; - if (!info->port->devs[0]) return; - for (i = 0; i < 6; i++) { - info->port->axes[0][i] = - ( info->data[(i << 2) + 1] << 12 | info->data[(i << 2) + 2] << 8 | - info->data[(i << 2) + 3] << 4 | info->data[(i << 2) + 4] ) - - 32768; - } - break; - - case 'e': /* Error packet */ - if (info->idx != 4) return; - if (js_mag_crunch_nibbles(info->data, 3)) return; - switch (info->data[1]) { - case 1: - printk(KERN_ERR "joy-magellan: Received command error packet. Failing command byte: %c\n", - info->data[2] | (info->data[3] << 4)); - break; - case 2: - printk(KERN_ERR "joy-magellan: Received framing error packet.\n"); - break; - default: - printk(KERN_ERR "joy-magellan: Received unknown error packet.\n"); - } - break; - - case 'k': /* Button data */ - if (info->idx != 4) return; - if (js_mag_crunch_nibbles(info->data, 3)) return; - if (!info->port->devs[0]) return; - info->port->buttons[0][0] = (info->data[1] << 1) | (info->data[2] << 5) | info->data[3]; - break; - - case 'm': /* Mode */ - if (info->idx != 2) return; - if (js_mag_crunch_nibbles(info->data, 1)) return; - break; - - case 'n': /* Null radius */ - if (info->idx != 2) return; - if (js_mag_crunch_nibbles(info->data, 1)) return; - break; - - case 'p': /* Data rate */ - if (info->idx != 3) return; - if (js_mag_crunch_nibbles(info->data, 2)) return; - break; - - case 'q': /* Sensitivity */ - if (info->idx != 3) return; - if (js_mag_crunch_nibbles(info->data, 2)) return; - break; - - case 'v': /* Version string */ - info->data[info->idx] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - memcpy(info->name, info->data + i, info->idx - i); - break; - - case 'z': /* Zero position */ - break; - - default: - printk("joy-magellan: Unknown packet %d length %d:", info->data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", info->data[i]); - printk("\n"); - return; - } - - info->ack = info->data[0]; -} - -/* - * js_mag_command() sends a command to the Magellan, and waits for - * acknowledge. - */ - -static int js_mag_command(struct js_mag_info *info, char *command, int timeout) -{ - info->ack = 0; - if (info->tty->driver.write(info->tty, 0, command, strlen(command)) != strlen(command)) return -1; - while (!info->ack && timeout--) mdelay(1); - return -(info->ack != command[0]); -} - -/* - * js_mag_setup() initializes the Magellan to sane state. Also works as - * a probe for Magellan existence. - */ - -static int js_mag_setup(struct js_mag_info *info) -{ - - if (js_mag_command(info, "vQ\r", 800)) /* Read version */ - return -1; - if (js_mag_command(info, "m3\r", 50)) /* Set full 3d mode */ - return -1; - if (js_mag_command(info, "pBB\r", 50)) /* Set 16 reports/second (max) */ - return -1; - if (js_mag_command(info, "z\r", 50)) /* Set zero position */ - return -1; - - return 0; -} - -/* - * js_mag_read() updates the axis and button data upon startup. - */ - -static int js_mag_read(struct js_mag_info *info) -{ - memset(info->port->axes[0],0, sizeof(int) * 6); /* Axes are 0 after zero postition cmd */ - - if (js_mag_command(info, "kQ\r", 50)) /* Read buttons */ - return -1; - - return 0; -} - -/* - * js_mag_open() is a callback from the joystick device open routine. - */ - -static int js_mag_open(struct js_dev *jd) -{ - struct js_mag_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_mag_close() is a callback from the joystick device release routine. - */ - -static int js_mag_close(struct js_dev *jd) -{ - struct js_mag_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_mag_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_mag_init_corr() initializes the correction values for the Magellan. - * It asumes gain setting of 0, question is, what we should do for higher - * gain settings ... - */ - -static void js_mag_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 6; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29) / 256; - corr[0][i].coef[3] = (1 << 29) / 256; - } -} - -/* - * js_mag_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. It looks for the Magellan, and if found, registers - * it as a joystick device. - */ - -static int js_mag_ldisc_open(struct tty_struct *tty) -{ - struct js_mag_info iniinfo; - struct js_mag_info *info = &iniinfo; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_mag_port = js_register_port(js_mag_port, info, 1, sizeof(struct js_mag_info), NULL); - - info = js_mag_port->info; - info->port = js_mag_port; - tty->disc_data = info; - - if (js_mag_setup(info)) { - js_mag_port = js_unregister_port(info->port); - return -ENODEV; - } - - printk(KERN_INFO "js%d: Magellan [%s] on %s%d\n", - js_register_device(js_mag_port, 0, 6, 9, "Magellan", THIS_MODULE, js_mag_open, js_mag_close), - info->name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); - - - js_mag_read(info); - js_mag_init_corr(js_mag_port->corr); - - MOD_INC_USE_COUNT; - - return 0; -} - -/* - * js_mag_ldisc_close() is the opposite of js_mag_ldisc_open() - */ - -static void js_mag_ldisc_close(struct tty_struct *tty) -{ - struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_mag_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_mag_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_mag_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_mag_info* info = (struct js_mag_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) - if (cp[i] == '\r') { - js_mag_process_packet(info); - info->idx = 0; - } else { - if (info->idx < JS_MAG_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - } -} - -/* - * js_mag_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_mag_ldisc_room(struct tty_struct *tty) -{ - return JS_MAG_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_mag_ldisc = { - magic: TTY_LDISC_MAGIC, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) - name: "magellan", -#endif - open: js_mag_ldisc_open, - close: js_mag_ldisc_close, - receive_buf: js_mag_ldisc_receive, - receive_room: js_mag_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_mag_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_MAG, &js_mag_ldisc)) { - printk(KERN_ERR "joy-magellan: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_MAG, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-pci.c b/drivers/char/joystick/joy-pci.c deleted file mode 100644 index fbf9125e5..000000000 --- a/drivers/char/joystick/joy-pci.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * joy-pci.c Version 0.4.0 - * - * Copyright (c) 1999 Raymond Ingles - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting the - * gameports on Trident 4DWave and Aureal Vortex soundcards, and - * analog joysticks connected to them. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/pci.h> -#include <linux/pci_ids.h> -#include <linux/init.h> - -MODULE_AUTHOR("Raymond Ingles <sorceror@tir.com>"); -MODULE_PARM(js_pci, "3-32i"); - -#define NUM_CARDS 8 -static int js_pci[NUM_CARDS * 4] __initdata = { -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0, - -1,0,0,0,-1,0,0,0,-1,0,0,0,-1,0,0,0 }; - -static struct js_port * js_pci_port __initdata = NULL; - -#include "joy-analog.h" - -struct js_pci_info; -typedef void (*js_pci_func)(struct js_pci_info *); - -struct js_pci_data { - int vendor; /* PCI Vendor ID */ - int model; /* PCI Model ID */ - int size; /* Memory / IO region size */ - int lcr; /* Aureal Legacy Control Register */ - int gcr; /* Gameport control register */ - int buttons; /* Buttons location */ - int axes; /* Axes start */ - int axsize; /* Axis field size */ - int axmax; /* Axis field max value */ - js_pci_func init; - js_pci_func cleanup; - char *name; -}; - -struct js_pci_info { - unsigned char *base; - struct pci_dev *pci_p; - __u32 lcr; - struct js_pci_data *data; - struct js_an_info an; -}; - -/* - * js_pci_*_init() sets the info->base field, disables legacy gameports, - * and enables the enhanced ones. - */ - -static void js_pci_4dwave_init(struct js_pci_info *info) -{ - info->base = ioremap(BASE_ADDRESS(info->pci_p, 1), info->data->size); - pci_read_config_word(info->pci_p, info->data->lcr, (unsigned short *)&info->lcr); - pci_write_config_word(info->pci_p, info->data->lcr, info->lcr & ~0x20); - writeb(0x80, info->base + info->data->gcr); -} - -static void js_pci_vortex_init(struct js_pci_info *info) -{ - info->base = ioremap(BASE_ADDRESS(info->pci_p, 0), info->data->size); - info->lcr = readl(info->base + info->data->lcr); - writel(info->lcr & ~0x8, info->base + info->data->lcr); - writel(0x40, info->base + info->data->gcr); -} - -/* - * js_pci_*_cleanup does the opposite of the above functions. - */ - -static void js_pci_4dwave_cleanup(struct js_pci_info *info) -{ - pci_write_config_word(info->pci_p, info->data->lcr, info->lcr); - writeb(0x00, info->base + info->data->gcr); - iounmap(info->base); -} - -static void js_pci_vortex_cleanup(struct js_pci_info *info) -{ - writel(info->lcr, info->base + info->data->lcr); - writel(0x00, info->base + info->data->gcr); - iounmap(info->base); -} - -static struct js_pci_data js_pci_data[] = -{{ PCI_VENDOR_ID_TRIDENT, 0x2000, 0x10000, 0x00044 ,0x00030, 0x00031, 0x00034, 2, 0xffff, - js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave DX" }, - { PCI_VENDOR_ID_TRIDENT, 0x2001, 0x10000, 0x00044, 0x00030, 0x00031, 0x00034, 2, 0xffff, - js_pci_4dwave_init, js_pci_4dwave_cleanup, "Trident 4DWave NX" }, - { PCI_VENDOR_ID_AUREAL, 0x0001, 0x40000, 0x1280c, 0x1100c, 0x11008, 0x11010, 4, 0x1fff, - js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex1" }, - { PCI_VENDOR_ID_AUREAL, 0x0002, 0x40000, 0x2a00c, 0x2880c, 0x28808, 0x28810, 4, 0x1fff, - js_pci_vortex_init, js_pci_vortex_cleanup, "Aureal Vortex2" }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL }}; - -/* - * js_pci_read() reads data from a PCI gameport. - */ - -static int js_pci_read(void *xinfo, int **axes, int **buttons) -{ - struct js_pci_info *info = xinfo; - int i; - - info->an.buttons = ~readb(info->base + info->data->buttons) >> 4; - - for (i = 0; i < 4; i++) - info->an.axes[i] = readw(info->base + info->data->axes + i * info->data->axsize); - - js_an_decode(&info->an, axes, buttons); - - return 0; -} - -static struct js_port * __init js_pci_probe(struct js_port *port, int type, int number, - struct pci_dev *pci_p, struct js_pci_data *data) -{ - int i; - unsigned char u; - int mask0, mask1, numdev; - struct js_pci_info iniinfo; - struct js_pci_info *info = &iniinfo; - - mask0 = mask1 = 0; - - for (i = 0; i < NUM_CARDS; i++) - if (js_pci[i * 4] == type && js_pci[i * 4 + 1] == number) { - mask0 = js_pci[i * 4 + 2]; - mask1 = js_pci[i * 4 + 3]; - if (!mask0 && !mask1) return port; - break; - } - - memset(info, 0, sizeof(struct js_pci_info)); - - info->data = data; - info->pci_p = pci_p; - data->init(info); - - mdelay(10); - js_pci_read(info, NULL, NULL); - - for (i = u = 0; i < 4; i++) - if (info->an.axes[i] < info->data->axmax) u |= 1 << i; - - if ((numdev = js_an_probe_devs(&info->an, u, mask0, mask1, port)) <= 0) - return port; - - port = js_register_port(port, info, numdev, sizeof(struct js_pci_info), js_pci_read); - - info = port->info; - - for (i = 0; i < numdev; i++) - printk(KERN_WARNING "js%d: %s on %s #%d\n", - js_register_device(port, i, js_an_axes(i, &info->an), js_an_buttons(i, &info->an), - js_an_name(i, &info->an), THIS_MODULE, NULL, NULL), js_an_name(i, &info->an), data->name, number); - - js_pci_read(info, port->axes, port->buttons); - js_an_init_corr(&info->an, port->axes, port->corr, 32); - - return port; -} - -#ifndef MODULE -int __init js_pci_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(NUM_CARDS*4); - for (i = 0; i <= ints[0] && i < NUM_CARDS*4; i++) - js_pci[i] = ints[i+1]; - return 1; -} -__setup("js_pci=", js_pci_setup); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_pci_init(void) -#endif -{ - struct pci_dev *pci_p = NULL; - int i, j; - - for (i = 0; js_pci_data[i].vendor; i++) - for (j = 0; (pci_p = pci_find_device(js_pci_data[i].vendor, js_pci_data[i].model, pci_p)); j++) - if (pci_enable_device(pci_p) == 0) - js_pci_port = js_pci_probe(js_pci_port, i, j, pci_p, js_pci_data + i); - - if (!js_pci_port) { -#ifdef MODULE - printk(KERN_WARNING "joy-pci: no joysticks found\n"); -#endif - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_pci_info *info; - - while (js_pci_port) { - for (i = 0; i < js_pci_port->ndevs; i++) - if (js_pci_port->devs[i]) - js_unregister_device(js_pci_port->devs[i]); - info = js_pci_port->info; - info->data->cleanup(info); - js_pci_port = js_unregister_port(js_pci_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-sidewinder.c b/drivers/char/joystick/joy-sidewinder.c deleted file mode 100644 index da11598f4..000000000 --- a/drivers/char/joystick/joy-sidewinder.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * joy-sidewinder.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Microsoft SideWinder digital joystick family. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -/* - * These are really magic values. Changing them can make a problem go away, - * as well as break everything. - */ - -#undef JS_SW_DEBUG - -#define JS_SW_START 400 /* The time we wait for the first bit [400 us] */ -#define JS_SW_STROBE 45 /* Max time per bit [45 us] */ -#define JS_SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ -#define JS_SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ -#define JS_SW_END 8 /* Number of bits before end of packet to kick */ -#define JS_SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ -#define JS_SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ -#define JS_SW_OK 64 /* Number of packet read successes to switch optimization back on */ -#define JS_SW_LENGTH 512 /* Max number of bits in a packet */ - -/* - * SideWinder joystick types ... - */ - -#define JS_SW_TYPE_3DP 1 -#define JS_SW_TYPE_F23 2 -#define JS_SW_TYPE_GP 3 -#define JS_SW_TYPE_PP 4 -#define JS_SW_TYPE_FFP 5 -#define JS_SW_TYPE_FSP 6 -#define JS_SW_TYPE_FFW 7 - -static int js_sw_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_sw_port __initdata = NULL; - -static struct { - int x; - int y; -} js_sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -struct js_sw_info { - int io; - int length; - int speed; - unsigned char type; - unsigned char bits; - unsigned char number; - unsigned char fail; - unsigned char ok; -}; - -/* - * Gameport speed. - */ - -unsigned int js_sw_io_speed = 0; - -/* - * js_sw_measure_speed() measures the gameport i/o speed. - */ - -static int __init js_sw_measure_speed(int io) -{ -#ifdef __i386__ - -#define GET_TIME(x) do { outb(0, 0x43); x = inb(0x40); x |= inb(0x40) << 8; } while (0) -#define DELTA(x,y) ((y)-(x)+((y)<(x)?1193180L/HZ:0)) - - unsigned int i, t, t1, t2, t3, tx; - unsigned long flags; - - tx = 1 << 30; - - for(i = 0; i < 50; i++) { - save_flags(flags); /* Yes, all CPUs */ - cli(); - GET_TIME(t1); - for(t = 0; t < 50; t++) inb(io); - GET_TIME(t2); - GET_TIME(t3); - restore_flags(flags); - udelay(i * 10); - if ((t = DELTA(t2,t1) - DELTA(t3,t2)) < tx) tx = t; - } - - return 59659 / t; - -#else - - unsigned int j, t = 0; - - j = jiffies; while (j == jiffies); - j = jiffies; while (j == jiffies) { t++; inb(0x201); } - - return t * HZ / 1000; - -#endif -} - -/* - * js_sw_read_packet() is a function which reads either a data packet, or an - * identification packet from a SideWinder joystick. Better don't try to - * understand this, since all the ugliness of the Microsoft Digital - * Overdrive protocol is concentrated in this function. If you really want - * to know how this works, first go watch a couple horror movies, so that - * you are well prepared, read US patent #5628686 and then e-mail me, - * and I'll send you an explanation. - * Vojtech <vojtech@suse.cz> - */ - -static int js_sw_read_packet(int io, int speed, unsigned char *buf, int length, int id) -{ - unsigned long flags; - int timeout, bitout, sched, i, kick, start, strobe; - unsigned char pending, u, v; - - i = -id; /* Don't care about data, only want ID */ - timeout = id ? (JS_SW_TIMEOUT * speed) >> 10 : 0; /* Set up global timeout for ID packet */ - kick = id ? (JS_SW_KICK * speed) >> 10 : 0; /* Set up kick timeout for ID packet */ - start = (JS_SW_START * speed) >> 10; - strobe = (JS_SW_STROBE * speed) >> 10; - bitout = start; - pending = 0; - sched = 0; - - __save_flags(flags); /* Quiet, please */ - __cli(); - - outb(0xff, io); /* Trigger */ - v = inb(io); - - do { - bitout--; - u = v; - v = inb(io); - } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ - - if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ - - while ((timeout > 0 || bitout > 0) && (i < length)) { - - timeout--; - bitout--; /* Decrement timers */ - sched--; - - u = v; - v = inb(io); - - if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ - if (i >= 0) /* Want this data */ - buf[i] = v >> 5; /* Store it */ - i++; /* Advance index */ - bitout = strobe; /* Extend timeout for next bit */ - } - - if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ - sched = kick; /* Schedule second trigger */ - kick = 0; /* Don't schedule next time on falling edge */ - pending = 1; /* Mark schedule */ - } - - if (pending && sched < 0 && (i > -JS_SW_END)) { /* Second trigger time */ - outb(0xff, io); /* Trigger */ - bitout = start; /* Long bit timeout */ - pending = 0; /* Unmark schedule */ - timeout = 0; /* Switch from global to bit timeouts */ - } - } - - __restore_flags(flags); /* Done - relax */ - -#ifdef JS_SW_DEBUG - { - int j; - printk(KERN_DEBUG "joy-sidewinder: Read %d triplets. [", i); - for (j = 0; j < i; j++) printk("%d", buf[j]); - printk("]\n"); - } -#endif - - return i; -} - -/* - * js_sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. - * Parameter 'pos' is bit number inside packet where to start at, 'num' is number - * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits - * is number of bits per triplet. - */ - -#define GB(pos,num,shift) js_sw_get_bits(buf, pos, num, shift, info->bits) - -static __u64 js_sw_get_bits(unsigned char *buf, int pos, int num, char shift, char bits) -{ - __u64 data = 0; - int tri = pos % bits; /* Start position */ - int i = pos / bits; - int bit = shift; - - while (num--) { - data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ - if (tri == bits) { - i++; /* Next triplet */ - tri = 0; - } - } - - return data; -} - -/* - * js_sw_init_digital() initializes a SideWinder 3D Pro joystick - * into digital mode. - */ - -static void js_sw_init_digital(int io, int speed) -{ - int seq[] = { 140, 140+725, 140+300, 0 }; - unsigned long flags; - int i, t; - - __save_flags(flags); - __cli(); - - i = 0; - do { - outb(0xff, io); /* Trigger */ - t = (JS_SW_TIMEOUT * speed) >> 10; - while ((inb(io) & 1) && t) t--; /* Wait for axis to fall back to 0 */ - udelay(seq[i]); /* Delay magic time */ - } while (seq[++i]); - - outb(0xff, io); /* Last trigger */ - - __restore_flags(flags); -} - -/* - * js_sw_parity() computes parity of __u64 - */ - -static int js_sw_parity(__u64 t) -{ - int x = t ^ (t >> 32); - x ^= x >> 16; - x ^= x >> 8; - x ^= x >> 4; - x ^= x >> 2; - x ^= x >> 1; - return x & 1; -} - -/* - * js_sw_ccheck() checks synchronization bits and computes checksum of nibbles. - */ - -static int js_sw_check(__u64 t) -{ - char sum = 0; - - if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ - return -1; - - while (t) { /* Sum */ - sum += t & 0xf; - t >>= 4; - } - - return sum & 0xf; -} - -/* - * js_sw_parse() analyzes SideWinder joystick data, and writes the results into - * the axes and buttons arrays. - */ - -static int js_sw_parse(unsigned char *buf, struct js_sw_info *info, int **axes, int **buttons) -{ - int hat, i; - - switch (info->type) { - - case JS_SW_TYPE_3DP: - case JS_SW_TYPE_F23: - - if (js_sw_check(GB(0,64,0)) || (hat = GB(6,1,3) | GB(60,3,0)) > 8) return -1; - - axes[0][0] = GB( 3,3,7) | GB(16,7,0); - axes[0][1] = GB( 0,3,7) | GB(24,7,0); - axes[0][2] = GB(35,2,7) | GB(40,7,0); - axes[0][3] = GB(32,3,7) | GB(48,7,0); - axes[0][4] = js_sw_hat_to_axis[hat].x; - axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~(GB(37,1,8) | GB(38,1,7) | GB(8,7,0)); - - return 0; - - case JS_SW_TYPE_GP: - - for (i = 0; i < info->number * 15; i += 15) { - - if (js_sw_parity(GB(i,15,0))) return -1; - - axes[i][0] = GB(i+3,1,0) - GB(i+2,1,0); - axes[i][1] = GB(i+0,1,0) - GB(i+1,1,0); - buttons[i][0] = ~GB(i+4,10,0); - - } - - return 0; - - case JS_SW_TYPE_PP: - case JS_SW_TYPE_FFP: - - if (!js_sw_parity(GB(0,48,0)) || (hat = GB(42,4,0)) > 8) return -1; - - axes[0][0] = GB( 9,10,0); - axes[0][1] = GB(19,10,0); - axes[0][2] = GB(36, 6,0); - axes[0][3] = GB(29, 7,0); - axes[0][4] = js_sw_hat_to_axis[hat].x; - axes[0][5] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~GB(0,9,0); - - return 0; - - case JS_SW_TYPE_FSP: - - if (!js_sw_parity(GB(0,43,0)) || (hat = GB(28,4,0)) > 8) return -1; - - axes[0][0] = GB( 0,10,0); - axes[0][1] = GB(16,10,0); - axes[0][2] = GB(32, 6,0); - axes[0][3] = js_sw_hat_to_axis[hat].x; - axes[0][4] = js_sw_hat_to_axis[hat].y; - buttons[0][0] = ~(GB(10,6,0) | GB(26,2,6) | GB(38,2,8)); - - return 0; - - case JS_SW_TYPE_FFW: - - if (!js_sw_parity(GB(0,33,0))) return -1; - - axes[0][0] = GB( 0,10,0); - axes[0][1] = GB(10, 6,0); - axes[0][2] = GB(16, 6,0); - buttons[0][0] = ~GB(22,8,0); - - return 0; - } - - return -1; -} - -/* - * js_sw_read() reads SideWinder joystick data, and reinitializes - * the joystick in case of persistent problems. This is the function that is - * called from the generic code to poll the joystick. - */ - -static int js_sw_read(void *xinfo, int **axes, int **buttons) -{ - struct js_sw_info *info = xinfo; - unsigned char buf[JS_SW_LENGTH]; - int i; - - i = js_sw_read_packet(info->io, info->speed, buf, info->length, 0); - - if (info->type <= JS_SW_TYPE_F23 && info->length == 66 && i != 66) { /* Broken packet, try to fix */ - - if (i == 64 && !js_sw_check(js_sw_get_bits(buf,0,64,0,1))) { /* Last init failed, 1 bit mode */ - printk(KERN_WARNING "joy-sidewinder: Joystick in wrong mode on %#x" - " - going to reinitialize.\n", info->io); - info->fail = JS_SW_FAIL; /* Reinitialize */ - i = 128; /* Bogus value */ - } - - if (i < 66 && GB(0,64,0) == GB(i*3-66,64,0)) /* 1 == 3 */ - i = 66; /* Everything is fine */ - - if (i < 66 && GB(0,64,0) == GB(66,64,0)) /* 1 == 2 */ - i = 66; /* Everything is fine */ - - if (i < 66 && GB(i*3-132,64,0) == GB(i*3-66,64,0)) { /* 2 == 3 */ - memmove(buf, buf + i - 22, 22); /* Move data */ - i = 66; /* Carry on */ - } - } - - if (i == info->length && !js_sw_parse(buf, info, axes, buttons)) { /* Parse data */ - - info->fail = 0; - info->ok++; - - if (info->type <= JS_SW_TYPE_F23 && info->length == 66 /* Many packets OK */ - && info->ok > JS_SW_OK) { - - printk(KERN_INFO "joy-sidewinder: No more trouble on %#x" - " - enabling optimization again.\n", info->io); - info->length = 22; - } - - return 0; - } - - info->ok = 0; - info->fail++; - - if (info->type <= JS_SW_TYPE_F23 && info->length == 22 /* Consecutive bad packets */ - && info->fail > JS_SW_BAD) { - - printk(KERN_INFO "joy-sidewinder: Many bit errors on %#x" - " - disabling optimization.\n", info->io); - info->length = 66; - } - - if (info->fail < JS_SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ - - printk(KERN_WARNING "joy-sidewinder: Too many bit errors on %#x" - " - reinitializing joystick.\n", info->io); - - if (!i && info->type <= JS_SW_TYPE_F23) { /* 3D Pro can be in analog mode */ - udelay(3 * JS_SW_TIMEOUT); - js_sw_init_digital(info->io, info->speed); - } - - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, 0); /* Read normal data packet */ - udelay(JS_SW_TIMEOUT); - js_sw_read_packet(info->io, info->speed, buf, JS_SW_LENGTH, i); /* Read ID packet, this initializes the stick */ - - info->fail = JS_SW_FAIL; - - return -1; -} - -/* - * js_sw_init_corr() initializes the correction values for - * SideWinders. - */ - -static void __init js_sw_init_corr(int num_axes, int type, int number, struct js_corr **corr) -{ - int i, j; - - for (i = 0; i < number; i++) { - - for (j = 0; j < num_axes; j++) { - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 8; - corr[i][j].coef[0] = 511 - 32; - corr[i][j].coef[1] = 512 + 32; - corr[i][j].coef[2] = (1 << 29) / (511 - 32); - corr[i][j].coef[3] = (1 << 29) / (511 - 32); - } - - switch (type) { - - case JS_SW_TYPE_3DP: - case JS_SW_TYPE_F23: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 4; - corr[i][2].coef[0] = 255 - 16; - corr[i][2].coef[1] = 256 + 16; - corr[i][2].coef[2] = (1 << 29) / (255 - 16); - corr[i][2].coef[3] = (1 << 29) / (255 - 16); - - j = 4; - - break; - - case JS_SW_TYPE_PP: - case JS_SW_TYPE_FFP: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 0; - corr[i][2].coef[0] = 31 - 2; - corr[i][2].coef[1] = 32 + 2; - corr[i][2].coef[2] = (1 << 29) / (31 - 2); - corr[i][2].coef[3] = (1 << 29) / (31 - 2); - - corr[i][3].type = JS_CORR_BROKEN; - corr[i][3].prec = 1; - corr[i][3].coef[0] = 63 - 4; - corr[i][3].coef[1] = 64 + 4; - corr[i][3].coef[2] = (1 << 29) / (63 - 4); - corr[i][3].coef[3] = (1 << 29) / (63 - 4); - - j = 4; - - break; - - case JS_SW_TYPE_FFW: - - corr[i][0].type = JS_CORR_BROKEN; - corr[i][0].prec = 2; - corr[i][0].coef[0] = 511 - 8; - corr[i][0].coef[1] = 512 + 8; - corr[i][0].coef[2] = (1 << 29) / (511 - 8); - corr[i][0].coef[3] = (1 << 29) / (511 - 8); - - corr[i][1].type = JS_CORR_BROKEN; - corr[i][1].prec = 1; - corr[i][1].coef[0] = 63; - corr[i][1].coef[1] = 63; - corr[i][1].coef[2] = (1 << 29) / -63; - corr[i][1].coef[3] = (1 << 29) / -63; - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 1; - corr[i][2].coef[0] = 63; - corr[i][2].coef[1] = 63; - corr[i][2].coef[2] = (1 << 29) / -63; - corr[i][2].coef[3] = (1 << 29) / -63; - - j = 3; - - break; - - case JS_SW_TYPE_FSP: - - corr[i][2].type = JS_CORR_BROKEN; - corr[i][2].prec = 0; - corr[i][2].coef[0] = 31 - 2; - corr[i][2].coef[1] = 32 + 2; - corr[i][2].coef[2] = (1 << 29) / (31 - 2); - corr[i][2].coef[3] = (1 << 29) / (31 - 2); - - j = 3; - - break; - - default: - - j = 0; - } - - for (; j < num_axes; j++) { /* Hats & other binary axes */ - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 0; - corr[i][j].coef[0] = 0; - corr[i][j].coef[1] = 0; - corr[i][j].coef[2] = (1 << 29); - corr[i][j].coef[3] = (1 << 29); - } - } -} - -/* - * js_sw_print_packet() prints the contents of a SideWinder packet. - */ - -static void js_sw_print_packet(char *name, int length, unsigned char *buf, char bits) -{ - int i; - - printk("joy-sidewinder: %s packet, %d bits. [", name, length); - for (i = (((length + 3) >> 2) - 1); i >= 0; i--) - printk("%x", (int)js_sw_get_bits(buf, i << 2, 4, 0, bits)); - printk("]\n"); -} - -/* - * js_sw_3dp_id() translates the 3DP id into a human legible string. - * Unfortunately I don't know how to do this for the other SW types. - */ - -static void js_sw_3dp_id(unsigned char *buf, char *comment) -{ - int i; - char pnp[8], rev[9]; - - for (i = 0; i < 7; i++) /* ASCII PnP ID */ - pnp[i] = js_sw_get_bits(buf, 24+8*i, 8, 0, 1); - - for (i = 0; i < 8; i++) /* ASCII firmware revision */ - rev[i] = js_sw_get_bits(buf, 88+8*i, 8, 0, 1); - - pnp[7] = rev[8] = 0; - - sprintf(comment, " [PnP %d.%02d id %s rev %s]", - (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | /* Two 6-bit values */ - js_sw_get_bits(buf, 16, 6, 0, 1)) / 100, - (int) (js_sw_get_bits(buf, 8, 6, 6, 1) | - js_sw_get_bits(buf, 16, 6, 0, 1)) % 100, - pnp, rev); -} - -/* - * js_sw_guess_mode() checks the upper two button bits for toggling - - * indication of that the joystick is in 3-bit mode. This is documented - * behavior for 3DP ID packet, and for example the FSP does this in - * normal packets instead. Fun ... - */ - -static int js_sw_guess_mode(unsigned char *buf, int len) -{ - int i; - unsigned char xor = 0; - for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; - return !!xor * 2 + 1; -} - -/* - * js_sw_probe() probes for SideWinder type joysticks. - */ - -static struct js_port __init *js_sw_probe(int io, struct js_port *port) -{ - struct js_sw_info info; - char *names[] = {NULL, "SideWinder 3D Pro", "Flight2000 F-23", "SideWinder GamePad", "SideWinder Precision Pro", - "SideWinder Force Feedback Pro", "SideWinder FreeStyle Pro", "SideWinder Force Feedback Wheel" }; - char axes[] = { 0, 6, 6, 2, 6, 6, 5, 3 }; - char buttons[] = { 0, 9, 9, 10, 9, 9, 10, 8 }; - int i, j, k, l, speed; - unsigned char buf[JS_SW_LENGTH]; - unsigned char idbuf[JS_SW_LENGTH]; - unsigned char m = 1; - char comment[40]; - - comment[0] = 0; - - if (check_region(io, 1)) return port; - - speed = js_sw_measure_speed(io); - - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read normal packet */ - m |= js_sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ - udelay(JS_SW_TIMEOUT); - -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 1: Mode %d. Length %d.\n", m , i); -#endif - - if (!i) { /* No data. 3d Pro analog mode? */ - js_sw_init_digital(io, speed); /* Switch to digital */ - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ - udelay(JS_SW_TIMEOUT); -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 1b: Length %d.\n", i); -#endif - if (!i) return port; /* No data -> FAIL */ - } - - j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i); /* Read ID. This initializes the stick */ - m |= js_sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ - -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2: Mode %d. ID Length %d.\n", m , j); -#endif - - if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Retry reading packet */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2b: Mode %d. Length %d.\n", m , i); -#endif - if (!i) return port; - udelay(JS_SW_TIMEOUT); - j = js_sw_read_packet(io, speed, idbuf, JS_SW_LENGTH, i);/* Retry reading ID */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 2c: ID Length %d.\n", j); -#endif - - } - - k = JS_SW_FAIL; /* Try JS_SW_FAIL times */ - l = 0; - - do { - k--; - udelay(JS_SW_TIMEOUT); - i = js_sw_read_packet(io, speed, buf, JS_SW_LENGTH, 0); /* Read data packet */ -#ifdef JS_SW_DEBUG - printk(KERN_DEBUG "joy-sidewinder: Init 3: Length %d.\n", i); -#endif - - if (i > l) { /* Longer? As we can only lose bits, it makes */ - /* no sense to try detection for a packet shorter */ - l = i; /* than the previous one */ - - info.number = 1; - info.io = io; - info.speed = speed; - info.length = i; - info.bits = m; - info.fail = 0; - info.ok = 0; - info.type = 0; - - switch (i * m) { - case 60: - info.number++; - case 45: /* Ambiguous packet length */ - if (j <= 40) { /* ID length less or eq 40 -> FSP */ - case 43: - info.type = JS_SW_TYPE_FSP; - break; - } - info.number++; - case 30: - info.number++; - case 15: - info.type = JS_SW_TYPE_GP; - break; - case 33: - case 31: - info.type = JS_SW_TYPE_FFW; - break; - case 48: /* Ambiguous */ - if (j == 14) { /* ID lenght 14*3 -> FFP */ - info.type = JS_SW_TYPE_FFP; - sprintf(comment, " [AC %s]", js_sw_get_bits(idbuf,38,1,0,3) ? "off" : "on"); - } else - info.type = JS_SW_TYPE_PP; - break; - case 198: - info.length = 22; - case 64: - info.type = JS_SW_TYPE_3DP; - if (j == 160) js_sw_3dp_id(idbuf, comment); - break; - } - } - - } while (k && !info.type); - - if (!info.type) { - printk(KERN_WARNING "joy-sidewinder: unknown joystick device detected " - "(io=%#x), contact <vojtech@suse.cz>\n", io); - js_sw_print_packet("ID", j * 3, idbuf, 3); - js_sw_print_packet("Data", i * m, buf, m); - return port; - } - -#ifdef JS_SW_DEBUG - js_sw_print_packet("ID", j * 3, idbuf, 3); - js_sw_print_packet("Data", i * m, buf, m); -#endif - - k = i; - - request_region(io, 1, "joystick (sidewinder)"); - - port = js_register_port(port, &info, info.number, sizeof(struct js_sw_info), js_sw_read); - - for (i = 0; i < info.number; i++) - printk(KERN_INFO "js%d: %s%s at %#x [%d ns res %d-bit id %d data %d]\n", - js_register_device(port, i, axes[info.type], buttons[info.type], - names[info.type], THIS_MODULE, NULL, NULL), names[info.type], comment, io, - 1000000 / speed, m, j, k); - - js_sw_init_corr(axes[info.type], info.type, info.number, port->corr); - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_sw_init(void) -#endif -{ - int *p; - - for (p = js_sw_port_list; *p; p++) js_sw_port = js_sw_probe(*p, js_sw_port); - if (js_sw_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-sidewinder: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - int i; - struct js_sw_info *info; - - while (js_sw_port) { - for (i = 0; i < js_sw_port->ndevs; i++) - if (js_sw_port->devs[i]) - js_unregister_device(js_sw_port->devs[i]); - info = js_sw_port->info; - release_region(info->io, 1); - js_sw_port = js_unregister_port(js_sw_port); - } - -} -#endif diff --git a/drivers/char/joystick/joy-spaceball.c b/drivers/char/joystick/joy-spaceball.c deleted file mode 100644 index 3ace13642..000000000 --- a/drivers/char/joystick/joy-spaceball.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * joy-spaceball.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * Copyright (c) 1999 Joseph Krahn - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the SpaceTec SpaceBall 4000 FLX. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_SBALL 12 -#define JS_SBALL_MAX_LENGTH 128 - -/* - * List of SpaceBalls. - */ - -static struct js_port* js_sball_port = NULL; - -/* - * Per-Ball data. - */ - -struct js_sball_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_SBALL_MAX_LENGTH]; - int js; - char used; -}; - -/* - * js_sball_process_packet() decodes packets the driver receives from the - * SpaceBall. - */ - -static void js_sball_process_packet(struct js_sball_info* info) -{ - int i,b; - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - - if (info->idx < 2) return; - - switch (info->data[0]) { - - case '@': /* Reset packet */ - info->data[info->idx - 1] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - - printk(KERN_INFO "js%d: SpaceBall 4000FLX [%s] on %s%d\n", - info->js, info->data + i, info->tty->driver.name, - MINOR(info->tty->device) - info->tty->driver.minor_start); - - memset(axes[0], 0, sizeof(int) * 6); /* All axes, buttons should be zero */ - buttons[0][0] = 0; - break; - - case 'D': /* Ball data */ - if (info->idx != 16) return; - if (!info->port->devs[0]) return; - axes[0][0] = ((data[3] << 8) | data[4] ); - axes[0][1] = ((data[5] << 8) | data[6] ); - axes[0][2] = ((data[7] << 8) | data[8] ); - axes[0][3] = ((data[9] << 8) | data[10]); - axes[0][4] = ((data[11]<< 8) | data[12]); - axes[0][5] = ((data[13]<< 8) | data[14]); - for(i = 0; i < 6; i ++) if (axes[0][i] & 0x8000) axes[0][i] -= 0x10000; - break; - - case 'K': /* Button data, part1 */ - /* We can ignore this packet for the SB 4000FLX. */ - break; - - case '.': /* Button data, part2 */ - if (info->idx != 4) return; - if (!info->port->devs[0]) return; - b = (data[1] & 0xbf) << 8 | (data[2] & 0xbf); - buttons[0][0] = ((b & 0x1f80) >> 1 | (b & 0x3f)); - break; - - case '?': /* Error packet */ - info->data[info->idx - 1] = 0; - printk(KERN_ERR "joy-spaceball: Device error. [%s]\n",info->data+1); - break; - - case 'A': /* reply to A command (ID# report) */ - case 'B': /* reply to B command (beep) */ - case 'H': /* reply to H command (firmware report) */ - case 'S': /* reply to S command (single beep) */ - case 'Y': /* reply to Y command (scale flag) */ - case '"': /* reply to "n command (report info, part n) */ - break; - - case 'P': /* Pulse (update) speed */ - if (info->idx != 3) return; /* data[2],data[3] = hex digits for speed 00-FF */ - break; - - default: - printk("joy-spaceball: Unknown packet %d length %d:", data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_sball_open() is a callback from the joystick device open routine. - */ - -static int js_sball_open(struct js_dev *jd) -{ - struct js_sball_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_sball_close() is a callback from the joystick device release routine. - */ - -static int js_sball_close(struct js_dev *jd) -{ - struct js_sball_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_sball_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_sball_init_corr() initializes the correction values for the SpaceBall. - */ - -static void __init js_sball_init_corr(struct js_corr **corr) -{ - int j; - - for (j = 0; j < 3; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = 50000; - corr[0][j].coef[3] = 50000; - } - for (j = 3; j < 6; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = 300000; - corr[0][j].coef[3] = 300000; - } -} - -/* - * js_sball_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_sball_ldisc_open(struct tty_struct *tty) -{ - struct js_sball_info iniinfo; - struct js_sball_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_sball_port = js_register_port(js_sball_port, info, 1, sizeof(struct js_sball_info), NULL); - - info = js_sball_port->info; - info->port = js_sball_port; - tty->disc_data = info; - - info->js = js_register_device(js_sball_port, 0, 6, 12, "SpaceBall 4000 FLX", THIS_MODULE, js_sball_open, js_sball_close); - - js_sball_init_corr(js_sball_port->corr); - - return 0; -} - -/* - * js_sball_ldisc_close() is the opposite of js_sball_ldisc_open() - */ - -static void js_sball_ldisc_close(struct tty_struct *tty) -{ - struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_sball_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_sball_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_sball_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_sball_info* info = (struct js_sball_info*) tty->disc_data; - int i; - int esc_flag = 0; - -/* - * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, - * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can - * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) - * on whether the axis value is increasing, decreasing, or same as before. - * (I don't see why this is useful). - * - * There may be a nicer whay to handle the escapes, but I wanted to be sure to - * allow for an escape at the end of the buffer. - */ - for (i = 0; i < count; i++) { - if (esc_flag) { /* If the last char was an escape, overwrite it with the escaped value */ - - switch (cp[i]){ - case 'M': - case 'Q': - case 'S': - info->data[info->idx]=0x0d; - break; - case '^': /* escaped escape; leave as is */ - break; - default: - printk("joy-spaceball: Unknown escape character: %02x\n", cp[i]); - } - - esc_flag = 0; - - } else { - - if (info->idx < JS_SBALL_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - - if (cp[i] == 0x0D) { - if (info->idx) - js_sball_process_packet(info); - info->idx = 0; - } else - if (cp[i] == '^') esc_flag = 1; - - } - } -} - -/* - * js_sball_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_sball_ldisc_room(struct tty_struct *tty) -{ - return JS_SBALL_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_sball_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "spaceball", - open: js_sball_ldisc_open, - close: js_sball_ldisc_close, - receive_buf: js_sball_ldisc_receive, - receive_room: js_sball_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_sball_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_SBALL, &js_sball_ldisc)) { - printk(KERN_ERR "joy-spaceball: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_SBALL, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-spaceorb.c b/drivers/char/joystick/joy-spaceorb.c deleted file mode 100644 index 0fb4f4c71..000000000 --- a/drivers/char/joystick/joy-spaceorb.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * joy-spaceorb.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the SpaceTec SpaceOrb 360 and SpaceBall Avenger 6dof controllers. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_ORB 15 -#define JS_ORB_MAX_LENGTH 64 - -/* - * List of SpaceOrbs. - */ - -static struct js_port* js_orb_port = NULL; - -/* - * Per-Orb data. - */ - -struct js_orb_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - unsigned char data[JS_ORB_MAX_LENGTH]; - int js; - char used; -}; - -static unsigned char js_orb_xor[] = "SpaceWare"; - -static unsigned char *js_orb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", - "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; - -/* - * js_orb_process_packet() decodes packets the driver receives from the - * SpaceOrb. - */ - -static void js_orb_process_packet(struct js_orb_info* info) -{ - int i; - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - unsigned char c = 0; - - if (info->idx < 2) return; - for (i = 0; i < info->idx; i++) c ^= data[i]; - if (c) return; - - switch (info->data[0]) { - - case 'R': /* Reset packet */ - info->data[info->idx - 1] = 0; - for (i = 1; i < info->idx && info->data[i] == ' '; i++); - printk(KERN_INFO "js%d: SpaceOrb 360 [%s] on %s%d\n", - info->js, info->data + i, info->tty->driver.name, - MINOR(info->tty->device) - info->tty->driver.minor_start); - break; - - case 'D': /* Ball + button data */ - if (info->idx != 12) return; - if (!info->port->devs[0]) return; - for (i = 0; i < 9; i++) info->data[i+2] ^= js_orb_xor[i]; - axes[0][0] = ( data[2] << 3) | (data[ 3] >> 4); - axes[0][1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); - axes[0][2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); - axes[0][3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); - axes[0][4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); - axes[0][5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); - for(i = 0; i < 6; i ++) if (axes[0][i] & 0x200) axes[0][i] -= 1024; - buttons[0][0] = data[1]; - break; - - case 'K': /* Button data */ - if (info->idx != 5) return; - if (!info->port->devs[0]) return; - buttons[0][0] = data[2]; - break; - - case 'E': /* Error packet */ - if (info->idx != 4) return; - printk(KERN_ERR "joy-spaceorb: Device error. [ "); - for (i = 0; i < 7; i++) - if (data[1] & (1 << i)) - printk("%s ", js_orb_errors[i]); - printk("]\n"); - break; - - case 'N': /* Null region */ - if (info->idx != 3) return; - break; - - case 'P': /* Pulse (update) speed */ - if (info->idx != 4) return; - break; - - default: - printk("joy-spaceorb: Unknown packet %d length %d:", data[0], info->idx); - for (i = 0; i < info->idx; i++) printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_orb_open() is a callback from the joystick device open routine. - */ - -static int js_orb_open(struct js_dev *jd) -{ - struct js_orb_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_orb_close() is a callback from the joystick device release routine. - */ - -static int js_orb_close(struct js_dev *jd) -{ - struct js_orb_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_orb_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_orb_init_corr() initializes the correction values for the SpaceOrb. - */ - -static void __init js_orb_init_corr(struct js_corr **corr) -{ - int j; - - for (j = 0; j < 6; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0 ; - corr[0][j].coef[1] = 0 ; - corr[0][j].coef[2] = (1 << 29) / 511; - corr[0][j].coef[3] = (1 << 29) / 511; - } -} - -/* - * js_orb_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_orb_ldisc_open(struct tty_struct *tty) -{ - struct js_orb_info iniinfo; - struct js_orb_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->used = 1; - - js_orb_port = js_register_port(js_orb_port, info, 1, sizeof(struct js_orb_info), NULL); - - info = js_orb_port->info; - info->port = js_orb_port; - tty->disc_data = info; - - info->js = js_register_device(js_orb_port, 0, 6, 7, "SpaceOrb 360", THIS_MODULE, js_orb_open, js_orb_close); - - js_orb_init_corr(js_orb_port->corr); - - return 0; -} - -/* - * js_orb_ldisc_close() is the opposite of js_orb_ldisc_open() - */ - -static void js_orb_ldisc_close(struct tty_struct *tty) -{ - struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_orb_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_orb_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_orb_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_orb_info* info = (struct js_orb_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) { - if (~cp[i] & 0x80) { - if (info->idx) js_orb_process_packet(info); - info->idx = 0; - } - if (info->idx < JS_ORB_MAX_LENGTH) - info->data[info->idx++] = cp[i] & 0x7f; - } -} - -/* - * js_orb_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_orb_ldisc_room(struct tty_struct *tty) -{ - return JS_ORB_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_orb_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "spaceorb", - open: js_orb_ldisc_open, - close: js_orb_ldisc_close, - receive_buf: js_orb_ldisc_receive, - receive_room: js_orb_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_orb_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_ORB, &js_orb_ldisc)) { - printk(KERN_ERR "joy-spaceorb: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_ORB, NULL); -} -#endif diff --git a/drivers/char/joystick/joy-thrustmaster.c b/drivers/char/joystick/joy-thrustmaster.c deleted file mode 100644 index 56e043a59..000000000 --- a/drivers/char/joystick/joy-thrustmaster.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * joy-thrustmaster.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * ThrustMaster DirectConnect (BSP) joystick family. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/init.h> - -#define JS_TM_MAX_START 400 -#define JS_TM_MAX_STROBE 45 -#define JS_TM_MAX_LENGTH 13 - -#define JS_TM_MODE_M3DI 1 -#define JS_TM_MODE_3DRP 3 -#define JS_TM_MODE_FGP 163 - -#define JS_TM_BYTE_ID 10 -#define JS_TM_BYTE_REV 11 -#define JS_TM_BYTE_DEF 12 - -static int js_tm_port_list[] __initdata = {0x201, 0}; -static struct js_port* js_tm_port __initdata = NULL; - -static unsigned char js_tm_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; -static unsigned char js_tm_byte_d[16] = { 2, 5, 8, 9 }; - -struct js_tm_info { - int io; - unsigned char mode; -}; - -/* - * js_tm_read_packet() reads a ThrustMaster packet. - */ - -static int js_tm_read_packet(int io, unsigned char *data) -{ - unsigned int t, p; - unsigned char u, v, error; - int i, j; - unsigned long flags; - - error = 0; - i = j = 0; - p = t = JS_TM_MAX_START; - - __save_flags(flags); - __cli(); - outb(0xff,io); - - v = inb(io) >> 4; - - do { - t--; - u = v; v = inb(io) >> 4; - if (~v & u & 2) { - if (j) { - if (j < 9) { /* Data bit */ - data[i] |= (~v & 1) << (j - 1); - j++; - } else { /* Stop bit */ - error |= v & 1; - j = 0; - i++; - } - } else { /* Start bit */ - data[i] = 0; - error |= ~v & 1; - j++; - } - p = t = (p - t) << 1; - } - } while (!error && i < JS_TM_MAX_LENGTH && t > 0); - - __restore_flags(flags); - - return -(i != JS_TM_MAX_LENGTH); -} - -/* - * js_tm_read() reads and analyzes ThrustMaster joystick data. - */ - -static int js_tm_read(void *xinfo, int **axes, int **buttons) -{ - struct js_tm_info *info = xinfo; - unsigned char data[JS_TM_MAX_LENGTH]; - int i; - - if (js_tm_read_packet(info->io, data)) return -1; - if (data[JS_TM_BYTE_ID] != info->mode) return -1; - - for (i = 0; i < data[JS_TM_BYTE_DEF] >> 4; i++) axes[0][i] = data[js_tm_byte_a[i]]; - - switch (info->mode) { - - case JS_TM_MODE_M3DI: - - axes[0][4] = ((data[js_tm_byte_d[0]] >> 3) & 1) - ((data[js_tm_byte_d[0]] >> 1) & 1); - axes[0][5] = ((data[js_tm_byte_d[0]] >> 2) & 1) - ( data[js_tm_byte_d[0]] & 1); - - buttons[0][0] = ((data[js_tm_byte_d[0]] >> 6) & 0x01) | ((data[js_tm_byte_d[0]] >> 3) & 0x06) - | ((data[js_tm_byte_d[0]] >> 4) & 0x08) | ((data[js_tm_byte_d[1]] >> 2) & 0x30); - - return 0; - - case JS_TM_MODE_3DRP: - case JS_TM_MODE_FGP: - - buttons[0][0] = (data[js_tm_byte_d[0]] & 0x3f) | ((data[js_tm_byte_d[1]] << 6) & 0xc0) - | (( ((int) data[js_tm_byte_d[0]]) << 2) & 0x300); - - return 0; - - default: - - buttons[0][0] = 0; - - for (i = 0; i < (data[JS_TM_BYTE_DEF] & 0xf); i++) - buttons[0][0] |= ((int) data[js_tm_byte_d[i]]) << (i << 3); - - return 0; - - } - - return -1; -} - -/* - * js_tm_init_corr() initializes the correction values for - * ThrustMaster joysticks. - */ - -static void __init js_tm_init_corr(int num_axes, int mode, int **axes, struct js_corr **corr) -{ - int j = 0; - - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 127 - 2; - corr[0][j].coef[1] = 128 + 2; - corr[0][j].coef[2] = (1 << 29) / (127 - 4); - corr[0][j].coef[3] = (1 << 29) / (127 - 4); - } - - switch (mode) { - case JS_TM_MODE_M3DI: j = 4; break; - default: break; - } - - for (; j < num_axes; j++) { - corr[0][j].type = JS_CORR_BROKEN; - corr[0][j].prec = 0; - corr[0][j].coef[0] = 0; - corr[0][j].coef[1] = 0; - corr[0][j].coef[2] = (1 << 29); - corr[0][j].coef[3] = (1 << 29); - } - -} - -/* - * js_tm_probe() probes for ThrustMaster type joysticks. - */ - -static struct js_port __init *js_tm_probe(int io, struct js_port *port) -{ - struct js_tm_info info; - struct js_rm_models { - unsigned char id; - char *name; - char axes; - char buttons; - } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 6 }, - { 3, "ThrustMaster Rage 3D Gamepad", 2, 10 }, - { 163, "Thrustmaster Fusion GamePad", 2, 10 }, - { 0, NULL, 0, 0 }}; - char name[64]; - unsigned char data[JS_TM_MAX_LENGTH]; - unsigned char a, b; - int i; - - if (check_region(io, 1)) return port; - - if (js_tm_read_packet(io, data)) return port; - - info.io = io; - info.mode = data[JS_TM_BYTE_ID]; - - if (!info.mode) return port; - - for (i = 0; models[i].id && models[i].id != info.mode; i++); - - if (models[i].id != info.mode) { - a = data[JS_TM_BYTE_DEF] >> 4; - b = (data[JS_TM_BYTE_DEF] & 0xf) << 3; - sprintf(name, "Unknown %d-axis, %d-button TM device %d", a, b, info.mode); - } else { - sprintf(name, models[i].name); - a = models[i].axes; - b = models[i].buttons; - } - - request_region(io, 1, "joystick (thrustmaster)"); - port = js_register_port(port, &info, 1, sizeof(struct js_tm_info), js_tm_read); - printk(KERN_INFO "js%d: %s revision %d at %#x\n", - js_register_device(port, 0, a, b, name, THIS_MODULE, NULL, NULL), name, data[JS_TM_BYTE_REV], io); - js_tm_init_corr(a, info.mode, port->axes, port->corr); - - return port; -} - -#ifdef MODULE -int init_module(void) -#else -int __init js_tm_init(void) -#endif -{ - int *p; - - for (p = js_tm_port_list; *p; p++) js_tm_port = js_tm_probe(*p, js_tm_port); - if (js_tm_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-thrustmaster: no joysticks found\n"); -#endif - - return -ENODEV; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_tm_info *info; - - while (js_tm_port) { - js_unregister_device(js_tm_port->devs[0]); - info = js_tm_port->info; - release_region(info->io, 1); - js_tm_port = js_unregister_port(js_tm_port); - } -} -#endif diff --git a/drivers/char/joystick/joy-turbografx.c b/drivers/char/joystick/joy-turbografx.c deleted file mode 100644 index c138a99d0..000000000 --- a/drivers/char/joystick/joy-turbografx.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * joy-turbografx.c Version 1.2 - * - * Copyright (c) 1998-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * Steffen Schwenke's <schwenke@burg-halle.de> TurboGraFX parallel port - * interface. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/ioport.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/init.h> - - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_PARM(js_tg, "2-8i"); -MODULE_PARM(js_tg_2, "2-8i"); -MODULE_PARM(js_tg_3, "2-8i"); - -#define JS_TG_BUTTON1 0x08 -#define JS_TG_UP 0x10 -#define JS_TG_DOWN 0x20 -#define JS_TG_LEFT 0x40 -#define JS_TG_RIGHT 0x80 - -#define JS_TG_BUTTON2 0x02 -#define JS_TG_BUTTON3 0x04 -#define JS_TG_BUTTON4 0x01 -#define JS_TG_BUTTON5 0x08 - -static struct js_port* js_tg_port __initdata = NULL; - -static int js_tg[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int js_tg_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; -static int js_tg_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; - -struct js_tg_info { - struct pardevice *port; /* parport device */ - int sticks; /* joysticks connected */ -}; - -/* - * js_tg_read() reads and analyzes tg joystick data. - */ - -static int js_tg_read(void *xinfo, int **axes, int **buttons) -{ - struct js_tg_info *info = xinfo; - int data1, data2, i; - - for (i = 0; i < 7; i++) - if ((info->sticks >> i) & 1) { - - JS_PAR_DATA_OUT(~(1 << i), info->port); - data1 = JS_PAR_STATUS(info->port) ^ ~JS_PAR_STATUS_INVERT; - data2 = JS_PAR_CTRL_IN(info->port) ^ JS_PAR_CTRL_INVERT; - - axes[i][0] = ((data1 & JS_TG_RIGHT) ? 1 : 0) - ((data1 & JS_TG_LEFT) ? 1 : 0); - axes[i][1] = ((data1 & JS_TG_DOWN ) ? 1 : 0) - ((data1 & JS_TG_UP ) ? 1 : 0); - - buttons[i][0] = ((data1 & JS_TG_BUTTON1) ? 0x01 : 0) | ((data2 & JS_TG_BUTTON2) ? 0x02 : 0) - | ((data2 & JS_TG_BUTTON3) ? 0x04 : 0) | ((data2 & JS_TG_BUTTON4) ? 0x08 : 0) - | ((data2 & JS_TG_BUTTON5) ? 0x10 : 0); - - } - - return 0; -} - -/* - * open callback: claim parport. - * FIXME: race possible here. - */ - -int js_tg_open(struct js_dev *dev) -{ - struct js_tg_info *info = dev->port->info; - - if (!MOD_IN_USE) { - if (parport_claim(info->port)) return -EBUSY; - JS_PAR_CTRL_OUT(0x04, info->port); - } - MOD_INC_USE_COUNT; - return 0; -} - -/* - * close callback: release parport - */ - -int js_tg_close(struct js_dev *dev) -{ - struct js_tg_info *info = dev->port->info; - - MOD_DEC_USE_COUNT; - if (!MOD_IN_USE) { - JS_PAR_CTRL_OUT(0x00, info->port); - parport_release(info->port); - } - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - struct js_tg_info *info; - int i; - - while (js_tg_port) { - for (i = 0; i < js_tg_port->ndevs; i++) - if (js_tg_port->devs[i]) - js_unregister_device(js_tg_port->devs[i]); - info = js_tg_port->info; - parport_unregister_device(info->port); - js_tg_port = js_unregister_port(js_tg_port); - } -} -#endif - -/* - * js_tg_init_corr() initializes correction values of - * tg gamepads. - */ - -static void __init js_tg_init_corr(int sticks, struct js_corr **corr) -{ - int i, j; - - for (i = 0; i < 7; i++) - if ((sticks >> i) & 1) - for (j = 0; j < 2; j++) { - corr[i][j].type = JS_CORR_BROKEN; - corr[i][j].prec = 0; - corr[i][j].coef[0] = 0; - corr[i][j].coef[1] = 0; - corr[i][j].coef[2] = (1 << 29); - corr[i][j].coef[3] = (1 << 29); - } -} - -/* - * js_tg_probe() probes for tg gamepads. - */ - -static struct js_port __init *js_tg_probe(int *config, struct js_port *port) -{ - struct js_tg_info iniinfo; - struct js_tg_info *info = &iniinfo; - struct parport *pp; - int i; - - if (config[0] < 0) return port; - - - if (config[0] > 0x10) - for (pp=parport_enumerate(); pp && (pp->base!=config[0]); pp=pp->next); - else - for (pp=parport_enumerate(); pp && (config[0]>0); pp=pp->next) config[0]--; - - if (!pp) { - printk(KERN_ERR "joy-tg: no such parport\n"); - return port; - } - - info->port = parport_register_device(pp, "joystick (turbografx)", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!info->port) - return port; - - port = js_register_port(port, info, 7, sizeof(struct js_tg_info), js_tg_read); - info = port->info; - - info->sticks = 0; - - for (i = 0; i < 7; i++) - if (config[i+1] > 0 && config[i+1] < 6) { - printk(KERN_INFO "js%d: Multisystem joystick on %s\n", - js_register_device(port, i, 2, config[i+1], "Multisystem joystick", NULL, js_tg_open, js_tg_close), - info->port->port->name); - info->sticks |= (1 << i); - } - - if (!info->sticks) { - parport_unregister_device(info->port); - return port; - } - - js_tg_init_corr(info->sticks, port->corr); - - return port; -} - -#ifndef MODULE -int __init js_tg_setup(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg[i] = ints[i+1]; - return 1; -} -int __init js_tg_setup_2(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_2[i] = ints[i+1]; - return 1; -} -int __init js_tg_setup_3(SETUP_PARAM) -{ - int i; - SETUP_PARSE(2); - for (i = 0; i <= ints[0] && i < 2; i++) js_tg_3[i] = ints[i+1]; - return 1; -} -__setup("js_tg=", js_tg_setup); -__setup("js_tg_2=", js_tg_setup_2); -__setup("js_tg_3=", js_tg_setup_3); -#endif - -#ifdef MODULE -int init_module(void) -#else -int __init js_tg_init(void) -#endif -{ - js_tg_port = js_tg_probe(js_tg, js_tg_port); - js_tg_port = js_tg_probe(js_tg_2, js_tg_port); - js_tg_port = js_tg_probe(js_tg_3, js_tg_port); - - if (js_tg_port) return 0; - -#ifdef MODULE - printk(KERN_WARNING "joy-tg: no joysticks specified\n"); -#endif - return -ENODEV; -} diff --git a/drivers/char/joystick/joy-warrior.c b/drivers/char/joystick/joy-warrior.c deleted file mode 100644 index 232699859..000000000 --- a/drivers/char/joystick/joy-warrior.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * joy-warrior.c Version 0.1 - * - * Copyright (c) 1998 David Thompson - * Copyright (c) 1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is a module for the Linux joystick driver, supporting - * the Logitech WingMan Warrior joystick. - */ - -/* - * This program is free warftware; 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/tty.h> -#include <linux/init.h> - -/* - * Constants. - */ - -#define N_JOYSTICK_WAR 13 -#define JS_WAR_MAX_LENGTH 16 - -/* - * List of Warriors. - */ - -static struct js_port* js_war_port = NULL; - -static char js_war_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; - -/* - * Per-Warrior data. - */ - -struct js_war_info { - struct tty_struct* tty; - struct js_port* port; - int idx; - int len; - unsigned char data[JS_WAR_MAX_LENGTH]; - char used; -}; - -/* - * js_war_process_packet() decodes packets the driver receives from the - * Warrior. It updates the data accordingly. - */ - -static void js_war_process_packet(struct js_war_info* info) -{ - int **axes = info->port->axes; - int **buttons = info->port->buttons; - unsigned char *data = info->data; - int i; - - if (!info->idx) return; - - switch ((data[0] >> 4) & 7) { - - case 1: /* Button data */ - if (!info->port->devs[0]) return; - buttons[0][0] = ((data[3] & 0xa) >> 1) | ((data[3] & 0x5) << 1); - return; - case 3: /* XY-axis info->data */ - if (!info->port->devs[0]) return; - axes[0][0] = ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5)); - axes[0][1] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); - return; - break; - case 5: /* Throttle, spinner, hat info->data */ - if (!info->port->devs[0]) return; - axes[0][2] = (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7); - axes[0][3] = (data[3] & 2 ? 1 : 0) - (info->data[3] & 1 ? 1 : 0); - axes[0][4] = (data[3] & 8 ? 1 : 0) - (info->data[3] & 4 ? 1 : 0); - axes[0][5] = (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5); - return; - case 2: /* Static status (Send !S to get one) */ - case 4: /* Dynamic status */ - return; - default: - printk("joy-warrior: Unknown packet %d length %d:", (data[0] >> 4) & 7, info->idx); - for (i = 0; i < info->idx; i++) - printk(" %02x", data[i]); - printk("\n"); - return; - } -} - -/* - * js_war_open() is a callback from the joystick device open routine. - */ - -static int js_war_open(struct js_dev *jd) -{ - struct js_war_info *info = jd->port->info; - info->used++; - return 0; -} - -/* - * js_war_close() is a callback from the joystick device release routine. - */ - -static int js_war_close(struct js_dev *jd) -{ - struct js_war_info *info = jd->port->info; - if (!--info->used) { - js_unregister_device(jd->port->devs[0]); - js_war_port = js_unregister_port(jd->port); - } - return 0; -} - -/* - * js_war_init_corr() initializes the correction values for the Warrior. - */ - -static void __init js_war_init_corr(struct js_corr **corr) -{ - int i; - - for (i = 0; i < 6; i++) { - corr[0][i].type = JS_CORR_BROKEN; - corr[0][i].prec = 0; - corr[0][i].coef[0] = -8; - corr[0][i].coef[1] = 8; - corr[0][i].coef[2] = (1 << 29) / (128 - 64); - corr[0][i].coef[3] = (1 << 29) / (128 - 64); - } - - corr[0][2].coef[2] = (1 << 29) / (128 - 16); - corr[0][2].coef[3] = (1 << 29) / (128 - 16); - - for (i = 3; i < 5; i++) { - corr[0][i].coef[0] = 0; - corr[0][i].coef[1] = 0; - corr[0][i].coef[2] = (1 << 29); - corr[0][i].coef[3] = (1 << 29); - } - - corr[0][5].prec = -1; - corr[0][5].coef[0] = 0; - corr[0][5].coef[1] = 0; - corr[0][5].coef[2] = (1 << 29) / 128; - corr[0][5].coef[3] = (1 << 29) / 128; -} - -/* - * js_war_ldisc_open() is the routine that is called upon setting our line - * discipline on a tty. - */ - -static int js_war_ldisc_open(struct tty_struct *tty) -{ - struct js_war_info iniinfo; - struct js_war_info *info = &iniinfo; - - MOD_INC_USE_COUNT; - - info->tty = tty; - info->idx = 0; - info->len = 0; - info->used = 1; - - js_war_port = js_register_port(js_war_port, info, 1, sizeof(struct js_war_info), NULL); - - info = js_war_port->info; - info->port = js_war_port; - tty->disc_data = info; - - printk(KERN_INFO "js%d: WingMan Warrior on %s%d\n", - js_register_device(js_war_port, 0, 6, 4, "WingMan Warrior", THIS_MODULE, js_war_open, js_war_close), - tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); - - js_war_init_corr(js_war_port->corr); - - return 0; -} - -/* - * js_war_ldisc_close() is the opposite of js_war_ldisc_open() - */ - -static void js_war_ldisc_close(struct tty_struct *tty) -{ - struct js_war_info* info = (struct js_war_info*) tty->disc_data; - if (!--info->used) { - js_unregister_device(info->port->devs[0]); - js_war_port = js_unregister_port(info->port); - } - MOD_DEC_USE_COUNT; -} - -/* - * js_war_ldisc_receive() is called by the low level driver when characters - * are ready for us. We then buffer them for further processing, or call the - * packet processing routine. - */ - -static void js_war_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct js_war_info* info = (struct js_war_info*) tty->disc_data; - int i; - - for (i = 0; i < count; i++) { - if (cp[i] & 0x80) { - if (info->idx) - js_war_process_packet(info); - info->idx = 0; - info->len = js_war_lengths[(cp[i] >> 4) & 7]; - } - - if (info->idx < JS_WAR_MAX_LENGTH) - info->data[info->idx++] = cp[i]; - - if (info->idx == info->len) { - if (info->idx) - js_war_process_packet(info); - info->idx = 0; - info->len = 0; - } - } -} - -/* - * js_war_ldisc_room() reports how much room we do have for receiving data. - * Although we in fact have infinite room, we need to specify some value - * here, so why not the size of our packet buffer. It's big anyway. - */ - -static int js_war_ldisc_room(struct tty_struct *tty) -{ - return JS_WAR_MAX_LENGTH; -} - -/* - * The line discipline structure. - */ - -static struct tty_ldisc js_war_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "warrior", - open: js_war_ldisc_open, - close: js_war_ldisc_close, - receive_buf: js_war_ldisc_receive, - receive_room: js_war_ldisc_room, -}; - -/* - * The functions for inserting/removing us as a module. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_war_init(void) -#endif -{ - if (tty_register_ldisc(N_JOYSTICK_WAR, &js_war_ldisc)) { - printk(KERN_ERR "joy-warrior: Error registering line discipline.\n"); - return -ENODEV; - } - - return 0; -} - -#ifdef MODULE -void cleanup_module(void) -{ - tty_register_ldisc(N_JOYSTICK_WAR, NULL); -} -#endif diff --git a/drivers/char/joystick/joystick.c b/drivers/char/joystick/joystick.c deleted file mode 100644 index c9bd56ce3..000000000 --- a/drivers/char/joystick/joystick.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * joystick.c Version 1.2 - * - * Copyright (c) 1996-1999 Vojtech Pavlik - * - * Sponsored by SuSE - */ - -/* - * This is the main joystick driver for Linux. It doesn't support any - * devices directly, rather is lets you use sub-modules to do that job. See - * Documentation/joystick.txt for more info. - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <asm/io.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/joystick.h> -#include <linux/devfs_fs_kernel.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/malloc.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/poll.h> -#include <linux/config.h> -#include <linux/init.h> - -/* - * Configurable parameters. - */ - -#define JS_REFRESH_TIME HZ/50 /* Time between two reads of joysticks (20ms) */ - -/* - * Exported symbols. - */ - -EXPORT_SYMBOL(js_register_port); -EXPORT_SYMBOL(js_unregister_port); -EXPORT_SYMBOL(js_register_device); -EXPORT_SYMBOL(js_unregister_device); - -/* - * Buffer macros. - */ - -#define ROT(A,B,C) ((((A)<(C))&&(((B)>(A))&&((B)<(C))))||(((A)>(C))&&(((B)>(A))||((B)<(C))))) -#define GOF(X) (((X)==JS_BUFF_SIZE-1)?0:(X)+1) -#define GOB(X) ((X)?(X)-1:JS_BUFF_SIZE-1) -#define DIFF(X,Y) ((X)>(Y)?(X)-(Y):(Y)-(X)) - -/* - * Global variables. - */ - -static struct JS_DATA_SAVE_TYPE js_comp_glue; -static struct js_port *js_port = NULL; -static struct js_dev *js_dev = NULL; -static struct timer_list js_timer; -spinlock_t js_lock = SPIN_LOCK_UNLOCKED; -static int js_use_count = 0; - -/* - * Module info. - */ - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); -MODULE_SUPPORTED_DEVICE("js"); - -/* - * js_correct() performs correction of raw joystick data. - */ - -static int js_correct(int value, struct js_corr *corr) -{ - switch (corr->type) { - case JS_CORR_NONE: - break; - case JS_CORR_BROKEN: - value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : - ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : - ((corr->coef[2] * (value - corr->coef[0])) >> 14); - break; - - default: - return 0; - } - - if (value < -32767) return -32767; - if (value > 32767) return 32767; - - return value; -} - -/* - * js_button() returns value of button number i. - */ - -static inline int js_button(int *buttons, int i) -{ - return (buttons[i >> 5] >> (i & 0x1f)) & 1; -} - -/* - * js_add_event() adds an event to the buffer. This requires additional - * queue post-processing done by js_sync_buff. - */ - -static void js_add_event(struct js_dev *jd, __u32 time, __u8 type, __u8 number, __s16 value) -{ - jd->buff[jd->ahead].time = time; - jd->buff[jd->ahead].type = type; - jd->buff[jd->ahead].number = number; - jd->buff[jd->ahead].value = value; - if (++jd->ahead == JS_BUFF_SIZE) jd->ahead = 0; -} - -/* - * js_flush_data() does the same as js_process_data, except for that it doesn't - * generate any events - it just copies the data from new to cur. - */ - -static void js_flush_data(struct js_dev *jd) -{ - int i; - - for (i = 0; i < ((jd->num_buttons - 1) >> 5) + 1; i++) - jd->cur.buttons[i] = jd->new.buttons[i]; - for (i = 0; i < jd->num_axes; i++) - jd->cur.axes[i] = jd->new.axes[i]; -} - -/* - * js_process_data() finds changes in button states and axis positions and adds - * them as events to the buffer. - */ - -static void js_process_data(struct js_dev *jd) -{ - int i, t; - - for (i = 0; i < jd->num_buttons; i++) - if ((t = js_button(jd->new.buttons, i)) != js_button(jd->cur.buttons, i)) { - js_add_event(jd, jiffies, JS_EVENT_BUTTON, i, t); - jd->cur.buttons[i >> 5] ^= (1 << (i & 0x1f)); - } - - for (i = 0; i < jd->num_axes; i++) { - t = js_correct(jd->new.axes[i], &jd->corr[i]); - if (((jd->corr[i].prec == -1) && t) || - ((DIFF(jd->new.axes[i], jd->cur.axes[i]) > jd->corr[i].prec) && - (t != js_correct(jd->cur.axes[i], &jd->corr[i])))) { - js_add_event(jd, jiffies, JS_EVENT_AXIS, i, t); - jd->cur.axes[i] = jd->new.axes[i]; - } - } -} - -/* - * js_sync_buff() checks for all overflows caused by recent additions to the buffer. - * These happen only if some process is reading the data too slowly. It - * wakes up any process waiting for data. - */ - -static void js_sync_buff(struct js_dev *jd) -{ - struct js_list *curl = jd->list; - - if (jd->bhead != jd->ahead) { - if(ROT(jd->bhead, jd->tail, jd->ahead) || (jd->tail == jd->bhead)) { - while (curl) { - if (ROT(jd->bhead, curl->tail, jd->ahead) || (curl->tail == jd->bhead)) { - curl->tail = jd->ahead; - curl->startup = 0; - } - curl = curl->next; - } - jd->tail = jd->ahead; - } - jd->bhead = jd->ahead; - wake_up_interruptible(&jd->wait); - } -} - -/* - * js_do_timer() acts as an interrupt replacement. It reads the data - * from all ports and then generates events for all devices. - */ - -static void js_do_timer(unsigned long data) -{ - struct js_port *curp = js_port; - struct js_dev *curd = js_dev; - unsigned long flags; - - while (curp) { - if (curp->read) - if (curp->read(curp->info, curp->axes, curp->buttons)) - curp->fail++; - curp->total++; - curp = curp->next; - } - - spin_lock_irqsave(&js_lock, flags); - - while (curd) { - if (data) { - js_process_data(curd); - js_sync_buff(curd); - } else { - js_flush_data(curd); - } - curd = curd->next; - } - - spin_unlock_irqrestore(&js_lock, flags); - - js_timer.expires = jiffies + JS_REFRESH_TIME; - add_timer(&js_timer); -} - -/* - * js_read() copies one or more entries from jsd[].buff to user - * space. - */ - -static ssize_t js_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - struct js_event *buff = (void *) buf; - struct js_list *curl; - struct js_dev *jd; - unsigned long blocks = count / sizeof(struct js_event); - int written = 0; - int new_tail, orig_tail; - int retval = 0; - unsigned long flags; - - curl = file->private_data; - jd = curl->dev; - orig_tail = curl->tail; - -/* - * Check user data. - */ - - if (!blocks) - return -EINVAL; - -/* - * Lock it. - */ - - spin_lock_irqsave(&js_lock, flags); - -/* - * Handle (non)blocking i/o. - */ - if (count != sizeof(struct JS_DATA_TYPE)) { - - if (GOF(curl->tail) == jd->bhead && curl->startup == jd->num_axes + jd->num_buttons) { - - __set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&jd->wait, &wait); - - while (GOF(curl->tail) == jd->bhead) { - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - spin_unlock_irqrestore(&js_lock, flags); - schedule(); - spin_lock_irqsave(&js_lock, flags); - - } - - current->state = TASK_RUNNING; - remove_wait_queue(&jd->wait, &wait); - } - - if (retval) { - spin_unlock_irqrestore(&js_lock, flags); - return retval; - } - -/* - * Initial state. - */ - - while (curl->startup < jd->num_axes + jd->num_buttons && written < blocks && !retval) { - - struct js_event tmpevent; - - if (curl->startup < jd->num_buttons) { - tmpevent.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - tmpevent.value = js_button(jd->cur.buttons, curl->startup); - tmpevent.number = curl->startup; - } else { - tmpevent.type = JS_EVENT_AXIS | JS_EVENT_INIT; - tmpevent.value = js_correct(jd->cur.axes[curl->startup - jd->num_buttons], - &jd->corr[curl->startup - jd->num_buttons]); - tmpevent.number = curl->startup - jd->num_buttons; - } - - tmpevent.time = jiffies * (1000/HZ); - - if (copy_to_user(&buff[written], &tmpevent, sizeof(struct js_event))) - retval = -EFAULT; - - curl->startup++; - written++; - } - -/* - * Buffer data. - */ - - while ((jd->bhead != (new_tail = GOF(curl->tail))) && (written < blocks) && !retval) { - - if (copy_to_user(&buff[written], &jd->buff[new_tail], sizeof(struct js_event))) - retval = -EFAULT; - if (put_user((__u32)(jd->buff[new_tail].time * (1000/HZ)), &buff[written].time)) - retval = -EFAULT; - - curl->tail = new_tail; - written++; - } - } - - else - -/* - * Handle version 0.x compatibility. - */ - - { - struct JS_DATA_TYPE data; - - data.buttons = jd->new.buttons[0]; - data.x = jd->num_axes < 1 ? 0 : - ((js_correct(jd->new.axes[0], &jd->corr[0]) / 256) + 128) >> js_comp_glue.JS_CORR.x; - data.y = jd->num_axes < 2 ? 0 : - ((js_correct(jd->new.axes[1], &jd->corr[1]) / 256) + 128) >> js_comp_glue.JS_CORR.y; - - retval = copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - - curl->startup = jd->num_axes + jd->num_buttons; - curl->tail = GOB(jd->bhead); - if (!retval) retval = sizeof(struct JS_DATA_TYPE); - } - -/* - * Check main tail and move it. - */ - - if (orig_tail == jd->tail) { - new_tail = curl->tail; - curl = jd->list; - while (curl && curl->tail != jd->tail) { - if (ROT(jd->bhead, new_tail, curl->tail) || - (jd->bhead == curl->tail)) new_tail = curl->tail; - curl = curl->next; - } - if (!curl) jd->tail = new_tail; - } - - spin_unlock_irqrestore(&js_lock, flags); - - return retval ? retval : written * sizeof(struct js_event); -} - -/* - * js_poll() does select() support. - */ - -static unsigned int js_poll(struct file *file, poll_table *wait) -{ - struct js_list *curl = file->private_data; - unsigned long flags; - int retval = 0; - poll_wait(file, &curl->dev->wait, wait); - spin_lock_irqsave(&js_lock, flags); - if (GOF(curl->tail) != curl->dev->bhead || - curl->startup < curl->dev->num_axes + curl->dev->num_buttons) retval = POLLIN | POLLRDNORM; - spin_unlock_irqrestore(&js_lock, flags); - return retval; -} - -/* - * js_ioctl handles misc ioctl calls. - */ - -static int js_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - struct js_list *curl; - struct js_dev *jd; - int len; - - curl = file->private_data; - jd = curl->dev; - - switch (cmd) { - -/* - * 0.x compatibility - */ - - case JS_SET_CAL: - return copy_from_user(&js_comp_glue.JS_CORR, (struct JS_DATA_TYPE *) arg, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_GET_CAL: - return copy_to_user((struct JS_DATA_TYPE *) arg, &js_comp_glue.JS_CORR, - sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; - case JS_SET_TIMEOUT: - return get_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - case JS_GET_TIMEOUT: - return put_user(js_comp_glue.JS_TIMEOUT, (int *) arg); - case JS_SET_TIMELIMIT: - return get_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - case JS_GET_TIMELIMIT: - return put_user(js_comp_glue.JS_TIMELIMIT, (long *) arg); - case JS_SET_ALL: - return copy_from_user(&js_comp_glue, (struct JS_DATA_SAVE_TYPE *) arg, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - case JS_GET_ALL: - return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &js_comp_glue, - sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; - -/* - * 1.x ioctl calls - */ - - case JSIOCGVERSION: - return put_user(JS_VERSION, (__u32 *) arg); - case JSIOCGAXES: - return put_user(jd->num_axes, (__u8 *) arg); - case JSIOCGBUTTONS: - return put_user(jd->num_buttons, (__u8 *) arg); - case JSIOCSCORR: - return copy_from_user(jd->corr, (struct js_corr *) arg, - sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; - case JSIOCGCORR: - return copy_to_user((struct js_corr *) arg, jd->corr, - sizeof(struct js_corr) * jd->num_axes) ? -EFAULT : 0; - default: - if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { - len = strlen(jd->name) + 1; - if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); - if (copy_to_user((char *) arg, jd->name, len)) return -EFAULT; - return len; - } - } - - return -EINVAL; -} - -/* - * js_open() performs necessary initialization and adds - * an entry to the linked list. - */ - -static int js_open(struct inode *inode, struct file *file) -{ - struct js_list *curl, *new; - struct js_dev *jd = js_dev; - int i = MINOR(inode->i_rdev); - unsigned long flags; - int result; - - if (MAJOR(inode->i_rdev) != JOYSTICK_MAJOR) - return -EINVAL; - - - spin_lock_irqsave(&js_lock, flags); - - while (i > 0 && jd) { - jd = jd->next; - i--; - } - - spin_unlock_irqrestore(&js_lock, flags); - - if (!jd) return -ENODEV; - - if (jd->owner) - __MOD_INC_USE_COUNT(jd->owner); - if (jd->open && (result = jd->open(jd))) { - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - return result; - } - - new = kmalloc(sizeof(struct js_list), GFP_KERNEL); - if (!new) { - if (jd->close) - jd->close(jd); - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - - return -ENOMEM; - } - - spin_lock_irqsave(&js_lock, flags); - - curl = jd->list; - - jd->list = new; - jd->list->next = curl; - jd->list->dev = jd; - jd->list->startup = 0; - jd->list->tail = GOB(jd->bhead); - file->private_data = jd->list; - - spin_unlock_irqrestore(&js_lock, flags); - - if (!js_use_count++) js_do_timer(0); - - return 0; -} - -/* - * js_release() removes an entry from list and deallocates memory - * used by it. - */ - -static int js_release(struct inode *inode, struct file *file) -{ - struct js_list *curl = file->private_data; - struct js_dev *jd = curl->dev; - struct js_list **curp = &jd->list; - int new_tail; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - while (*curp && (*curp != curl)) curp = &((*curp)->next); - *curp = (*curp)->next; - - if (jd->list) - if (curl->tail == jd->tail) { - curl = jd->list; - new_tail = curl->tail; - while (curl && curl->tail != jd->tail) { - if (ROT(jd->bhead, new_tail, curl->tail) || - (jd->bhead == curl->tail)) new_tail = curl->tail; - curl = curl->next; - } - if (!curl) jd->tail = new_tail; - } - - spin_unlock_irqrestore(&js_lock, flags); - - kfree(file->private_data); - - if (!--js_use_count) del_timer(&js_timer); - - if (jd->close) - jd->close(jd); - if (jd->owner) - __MOD_DEC_USE_COUNT(jd->owner); - - return 0; -} - -/* - * js_dump_mem() dumps all data structures in memory. - * It's used for debugging only. - */ - -struct js_port *js_register_port(struct js_port *port, - void *info, int devs, int infos, js_read_func read) -{ - struct js_port **ptrp = &js_port; - struct js_port *curp; - void *all; - int i; - unsigned long flags; - - if (!(all = kmalloc(sizeof(struct js_port) + 4 * devs * sizeof(void*) + infos, GFP_KERNEL))) - return NULL; - - curp = all; - - curp->next = NULL; - curp->prev = port; - curp->read = read; - curp->ndevs = devs; - curp->fail = 0; - curp->total = 0; - - curp->devs = all += sizeof(struct js_port); - for (i = 0; i < devs; i++) curp->devs[i] = NULL; - - curp->axes = all += devs * sizeof(void*); - curp->buttons = (void*) all += devs * sizeof(void*); - curp->corr = all += devs * sizeof(void*); - - if (infos) { - curp->info = all += devs * sizeof(void*); - memcpy(curp->info, info, infos); - } else { - curp->info = NULL; - } - - spin_lock_irqsave(&js_lock, flags); - - while (*ptrp) ptrp=&((*ptrp)->next); - *ptrp = curp; - - spin_unlock_irqrestore(&js_lock, flags); - - return curp; -} - -struct js_port *js_unregister_port(struct js_port *port) -{ - struct js_port **curp = &js_port; - struct js_port *prev; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - printk("js: There were %d failures out of %d read attempts.\n", port->fail, port->total); - - while (*curp && (*curp != port)) curp = &((*curp)->next); - *curp = (*curp)->next; - - spin_unlock_irqrestore(&js_lock, flags); - - prev = port->prev; - kfree(port); - - return prev; -} - -extern struct file_operations js_fops; - -static devfs_handle_t devfs_handle = NULL; - -int js_register_device(struct js_port *port, int number, int axes, - int buttons, char *name, struct module *owner, - js_ops_func open, js_ops_func close) -{ - struct js_dev **ptrd = &js_dev; - struct js_dev *curd; - void *all; - int i = 0; - unsigned long flags; - char devfs_name[8]; - - if (!(all = kmalloc(sizeof(struct js_dev) + 2 * axes * sizeof(int) + - 2 * (((buttons - 1) >> 5) + 1) * sizeof(int) + - axes * sizeof(struct js_corr) + strlen(name) + 1, GFP_KERNEL))) - return -1; - - curd = all; - - curd->next = NULL; - curd->list = NULL; - curd->port = port; - curd->open = open; - curd->close = close; - curd->owner = owner; - - init_waitqueue_head(&curd->wait); - - curd->ahead = 0; - curd->bhead = 0; - curd->tail = JS_BUFF_SIZE - 1; - curd->num_axes = axes; - curd->num_buttons = buttons; - - curd->cur.axes = all += sizeof(struct js_dev); - curd->cur.buttons = all += axes * sizeof(int); - curd->new.axes = all += (((buttons - 1) >> 5) + 1) * sizeof(int); - curd->new.buttons = all += axes * sizeof(int); - curd->corr = all += (((buttons -1 ) >> 5) + 1) * sizeof(int); - - curd->name = all += axes * sizeof(struct js_corr); - strcpy(curd->name, name); - - port->devs[number] = curd; - port->axes[number] = curd->new.axes; - port->buttons[number] = curd->new.buttons; - port->corr[number] = curd->corr; - - spin_lock_irqsave(&js_lock, flags); - - while (*ptrd) { ptrd=&(*ptrd)->next; i++; } - *ptrd = curd; - - spin_unlock_irqrestore(&js_lock, flags); - - sprintf(devfs_name, "js%d", i); - curd->devfs_handle = devfs_register(devfs_handle, devfs_name, 0, - DEVFS_FL_DEFAULT, - JOYSTICK_MAJOR, i, - S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, - &js_fops, NULL); - - return i; -} - -void js_unregister_device(struct js_dev *dev) -{ - struct js_dev **curd = &js_dev; - unsigned long flags; - - spin_lock_irqsave(&js_lock, flags); - - while (*curd && (*curd != dev)) curd = &((*curd)->next); - *curd = (*curd)->next; - - spin_unlock_irqrestore(&js_lock, flags); - - devfs_unregister(dev->devfs_handle); - kfree(dev); -} - -/* - * The operations structure. - */ - -static struct file_operations js_fops = -{ - owner: THIS_MODULE, - read: js_read, - poll: js_poll, - ioctl: js_ioctl, - open: js_open, - release: js_release, -}; - -/* - * js_init() registers the driver and calls the probe function. - * also initializes some crucial variables. - */ - -#ifdef MODULE -int init_module(void) -#else -int __init js_init(void) -#endif -{ - - if (devfs_register_chrdev(JOYSTICK_MAJOR, "js", &js_fops)) { - printk(KERN_ERR "js: unable to get major %d for joystick\n", JOYSTICK_MAJOR); - return -EBUSY; - } - devfs_handle = devfs_mk_dir(NULL, "joysticks", 9, NULL); - - printk(KERN_INFO "js: Joystick driver v%d.%d.%d (c) 1999 Vojtech Pavlik <vojtech@suse.cz>\n", - JS_VERSION >> 16 & 0xff, JS_VERSION >> 8 & 0xff, JS_VERSION & 0xff); - - spin_lock_init(&js_lock); - - init_timer(&js_timer); - js_timer.function = js_do_timer; - js_timer.data = 1; - - memset(&js_comp_glue, 0, sizeof(struct JS_DATA_SAVE_TYPE)); - js_comp_glue.JS_TIMEOUT = JS_DEF_TIMEOUT; - js_comp_glue.JS_TIMELIMIT = JS_DEF_TIMELIMIT; - -#ifndef MODULE -#ifdef CONFIG_JOY_PCI - js_pci_init(); -#endif -#ifdef CONFIG_JOY_LIGHTNING - js_l4_init(); -#endif -#ifdef CONFIG_JOY_SIDEWINDER - js_sw_init(); -#endif -#ifdef CONFIG_JOY_ASSASSIN - js_as_init(); -#endif -#ifdef CONFIG_JOY_LOGITECH - js_lt_init(); -#endif -#ifdef CONFIG_JOY_THRUSTMASTER - js_tm_init(); -#endif -#ifdef CONFIG_JOY_GRAVIS - js_gr_init(); -#endif -#ifdef CONFIG_JOY_CREATIVE - js_cr_init(); -#endif -#ifdef CONFIG_JOY_ANALOG - js_an_init(); -#endif -#ifdef CONFIG_JOY_CONSOLE - js_console_init(); -#endif -#ifdef CONFIG_JOY_DB9 - js_db9_init(); -#endif -#ifdef CONFIG_JOY_TURBOGRAFX - js_tg_init(); -#endif -#ifdef CONFIG_JOY_AMIGA - js_am_init(); -#endif -#ifdef CONFIG_JOY_MAGELLAN - js_mag_init(); -#endif -#ifdef CONFIG_JOY_WARRIOR - js_war_init(); -#endif -#ifdef CONFIG_JOY_SPACEORB - js_orb_init(); -#endif -#ifdef CONFIG_JOY_SPACEBALL - js_sball_init(); -#endif -#endif - - return 0; -} - -/* - * cleanup_module() handles module removal. - */ - -#ifdef MODULE -void cleanup_module(void) -{ - del_timer(&js_timer); - devfs_unregister(devfs_handle); - if (devfs_unregister_chrdev(JOYSTICK_MAJOR, "js")) - printk(KERN_ERR "js: can't unregister device\n"); -} -#endif - diff --git a/drivers/char/joystick/lightning.c b/drivers/char/joystick/lightning.c new file mode 100644 index 000000000..69dfd1112 --- /dev/null +++ b/drivers/char/joystick/lightning.c @@ -0,0 +1,300 @@ +/* + * $Id: lightning.c,v 1.7 2000/05/24 19:36:03 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * PDPI Lightning 4 gamecard driver for Linux. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> + +#define L4_PORT 0x201 +#define L4_SELECT_ANALOG 0xa4 +#define L4_SELECT_DIGITAL 0xa5 +#define L4_SELECT_SECONDARY 0xa6 +#define L4_CMD_ID 0x80 +#define L4_CMD_GETCAL 0x92 +#define L4_CMD_SETCAL 0x93 +#define L4_ID 0x04 +#define L4_BUSY 0x01 +#define L4_TIMEOUT 80 /* 80 us */ + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +struct l4 { + struct gameport gameport; + unsigned char port; +} *l4_port[8]; + +/* + * l4_wait_ready() waits for the L4 to become ready. + */ + +static int l4_wait_ready(void) +{ + unsigned int t; + t = L4_TIMEOUT; + while ((inb(L4_PORT) & L4_BUSY) && t > 0) t--; + return -(t<=0); +} + +/* + * l4_cooked_read() reads data from the Lightning 4. + */ + +static int l4_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct l4 *l4 = gameport->driver; + unsigned char status; + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (l4->port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(l4->port & 3, L4_PORT); + + if (l4_wait_ready()) goto fail; + status = inb(L4_PORT); + + for (i = 0; i < 4; i++) + if (status & (1 << i)) { + if (l4_wait_ready()) goto fail; + axes[i] = inb(L4_PORT); + if (axes[i] > 252) axes[i] = -1; + } + + if (status & 0x10) { + if (l4_wait_ready()) goto fail; + *buttons = inb(L4_PORT) & 0x0f; + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +static int l4_open(struct gameport *gameport, int mode) +{ + struct l4 *l4 = gameport->driver; + if (l4->port != 0 && mode != GAMEPORT_MODE_COOKED) + return -1; + outb(L4_SELECT_ANALOG, L4_PORT); + return 0; +} + +/* + * l4_getcal() reads the L4 with calibration values. + */ + +static int l4_getcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(L4_CMD_GETCAL, L4_PORT); + + if (l4_wait_ready()) goto fail; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail; + + if (l4_wait_ready()) goto fail; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) goto fail; + cal[i] = inb(L4_PORT); + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_setcal() programs the L4 with calibration values. + */ + +static int l4_setcal(int port, int *cal) +{ + int i, result = -1; + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + (port >> 2), L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) goto fail; + outb(L4_CMD_SETCAL, L4_PORT); + + if (l4_wait_ready()) goto fail; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + (port >> 2)) goto fail; + + if (l4_wait_ready()) goto fail; + outb(port & 3, L4_PORT); + + for (i = 0; i < 4; i++) { + if (l4_wait_ready()) goto fail; + outb(cal[i], L4_PORT); + } + + result = 0; + +fail: outb(L4_SELECT_ANALOG, L4_PORT); + return result; +} + +/* + * l4_calibrate() calibrates the L4 for the attached device, so + * that the device's resistance fits into the L4's 8-bit range. + */ + +static int l4_calibrate(struct gameport *gameport, int *axes, int *max) +{ + int i, t; + int cal[4]; + struct l4 *l4 = gameport->driver; + + if (l4_getcal(l4->port, cal)) + return -1; + + for (i = 0; i < 4; i++) { + t = (max[i] * cal[i]) / 200; + t = (t < 1) ? 1 : ((t > 255) ? 255 : t); + axes[i] = (axes[i] < 0) ? -1 : (axes[i] * cal[i]) / t; + axes[i] = (axes[i] > 252) ? 252 : axes[i]; + cal[i] = t; + } + + if (l4_setcal(l4->port, cal)) + return -1; + + return 0; +} + +int __init l4_init(void) +{ + int cal[4] = {255,255,255,255}; + int i, j, rev, cards = 0; + struct gameport *gameport; + struct l4 *l4; + + if (!request_region(L4_PORT, 1, "lightning")) + return -1; + + for (i = 0; i < 2; i++) { + + outb(L4_SELECT_ANALOG, L4_PORT); + outb(L4_SELECT_DIGITAL + i, L4_PORT); + + if (inb(L4_PORT) & L4_BUSY) continue; + outb(L4_CMD_ID, L4_PORT); + + if (l4_wait_ready()) continue; + if (inb(L4_PORT) != L4_SELECT_DIGITAL + i) continue; + + if (l4_wait_ready()) continue; + if (inb(L4_PORT) != L4_ID) continue; + + if (l4_wait_ready()) continue; + rev = inb(L4_PORT); + + if (!rev) continue; + + if (!(l4_port[i * 4] = kmalloc(sizeof(struct l4) * 4, GFP_KERNEL))) { + printk(KERN_ERR "lightning: Out of memory allocating ports.\n"); + continue; + } + memset(l4_port[i * 4], 0, sizeof(struct l4) * 4); + + for (j = 0; j < 4; j++) { + + l4 = l4_port[i * 4 + j] = l4_port[i * 4] + j; + l4->port = i * 4 + j; + + gameport = &l4->gameport; + gameport->driver = l4; + gameport->open = l4_open; + gameport->cooked_read = l4_cooked_read; + gameport->calibrate = l4_calibrate; + gameport->type = GAMEPORT_EXT; + + if (!i && !j) { + gameport->io = L4_PORT; + gameport->size = 1; + } + + if (rev > 0x28) /* on 2.9+ the setcal command works correctly */ + l4_setcal(l4->port, cal); + + gameport_register_port(gameport); + } + + printk(KERN_INFO "gameport%d,%d,%d,%d: PDPI Lightning 4 %s card v%d.%d at %#x\n", + l4_port[i * 4 + 0]->gameport.number, l4_port[i * 4 + 1]->gameport.number, + l4_port[i * 4 + 2]->gameport.number, l4_port[i * 4 + 3]->gameport.number, + i ? "secondary" : "primary", rev >> 4, rev, L4_PORT); + + cards++; + } + + outb(L4_SELECT_ANALOG, L4_PORT); + + if (!cards) { + release_region(L4_PORT, 1); + return -1; + } + + return 0; +} + +void __init l4_exit(void) +{ + int i; + int cal[4] = {59, 59, 59, 59}; + + for (i = 0; i < 8; i++) + if (l4_port[i]) { + l4_setcal(l4_port[i]->port, cal); + gameport_unregister_port(&l4_port[i]->gameport); + } + outb(L4_SELECT_ANALOG, L4_PORT); + release_region(L4_PORT, 1); +} + +module_init(l4_init); +module_exit(l4_exit); diff --git a/drivers/char/joystick/magellan.c b/drivers/char/joystick/magellan.c new file mode 100644 index 000000000..e8c77f48e --- /dev/null +++ b/drivers/char/joystick/magellan.c @@ -0,0 +1,210 @@ +/* + * $Id: magellan.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Magellan and Space Mouse 6dof controller driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +/* + * Definitions & global arrays. + */ + +#define MAGELLAN_MAX_LENGTH 32 + +static int magellan_buttons[] = { BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8}; +static int magellan_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; +static char *magellan_name = "LogiCad3D Magellan"; + +/* + * Per-Magellan data. + */ + +struct magellan { + struct input_dev dev; + int idx; + unsigned char data[MAGELLAN_MAX_LENGTH]; +}; + +/* + * magellan_crunch_nibbles() verifies that the bytes sent from the Magellan + * have correct upper nibbles for the lower ones, if not, the packet will + * be thrown away. It also strips these upper halves to simplify further + * processing. + */ + +static int magellan_crunch_nibbles(unsigned char *data, int count) +{ + static unsigned char nibbles[16] = "0AB3D56GH9:K<MN?"; + + do { + if (data[count] == nibbles[data[count] & 0xf]) + data[count] = data[count] & 0xf; + else + return -1; + } while (--count); + + return 0; +} + +static void magellan_process_packet(struct magellan* magellan) +{ + struct input_dev *dev = &magellan->dev; + unsigned char *data = magellan->data; + int i, t; + + if (!magellan->idx) return; + + switch (magellan->data[0]) { + + case 'd': /* Axis data */ + if (magellan->idx != 25) return; + if (magellan_crunch_nibbles(data, 24)) return; + for (i = 0; i < 6; i++) + input_report_abs(dev, magellan_axes[i], + (data[(i << 2) + 1] << 12 | data[(i << 2) + 2] << 8 | + data[(i << 2) + 3] << 4 | data[(i << 2) + 4]) - 32768); + break; + + case 'k': /* Button data */ + if (magellan->idx != 4) return; + if (magellan_crunch_nibbles(data, 3)) return; + t = (data[1] << 1) | (data[2] << 5) | data[3]; + for (i = 0; i < 9; i++) input_report_key(dev, magellan_buttons[i], (t >> i) & 1); + break; + } +} + +static void magellan_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct magellan* magellan = serio->private; + + if (data == '\r') { + magellan_process_packet(magellan); + magellan->idx = 0; + } else { + if (magellan->idx < MAGELLAN_MAX_LENGTH) + magellan->data[magellan->idx++] = data; + } +} + +/* + * magellan_disconnect() is the opposite of magellan_connect() + */ + +static void magellan_disconnect(struct serio *serio) +{ + struct magellan* magellan = serio->private; + input_unregister_device(&magellan->dev); + serio_close(serio); + kfree(magellan); +} + +/* + * magellan_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Magellan, and if found, registers + * it as an input device. + */ + +static void magellan_connect(struct serio *serio, struct serio_dev *dev) +{ + struct magellan *magellan; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_MAGELLAN)) + return; + + if (!(magellan = kmalloc(sizeof(struct magellan), GFP_KERNEL))) + return; + + memset(magellan, 0, sizeof(struct magellan)); + + magellan->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < 9; i++) + set_bit(magellan_buttons[i], &magellan->dev.keybit); + + for (i = 0; i < 6; i++) { + t = magellan_axes[i]; + set_bit(t, magellan->dev.absbit); + magellan->dev.absmin[t] = -360; + magellan->dev.absmax[t] = 360; + } + + magellan->dev.private = magellan; + magellan->dev.name = magellan_name; + magellan->dev.idbus = BUS_RS232; + magellan->dev.idvendor = SERIO_MAGELLAN; + magellan->dev.idproduct = 0x0001; + magellan->dev.idversion = 0x0100; + + serio->private = magellan; + + if (serio_open(serio, dev)) { + kfree(magellan); + return; + } + + input_register_device(&magellan->dev); + + printk(KERN_INFO "input%d: %s on serio%d\n", magellan->dev.number, magellan_name, serio->number); +} + +/* + * The serio device structure. + */ + +static struct serio_dev magellan_dev = { + interrupt: magellan_interrupt, + connect: magellan_connect, + disconnect: magellan_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init magellan_init(void) +{ + serio_register_device(&magellan_dev); + return 0; +} + +void __exit magellan_exit(void) +{ + serio_unregister_device(&magellan_dev); +} + +module_init(magellan_init); +module_exit(magellan_exit); diff --git a/drivers/char/joystick/ns558.c b/drivers/char/joystick/ns558.c new file mode 100644 index 000000000..f34a18640 --- /dev/null +++ b/drivers/char/joystick/ns558.c @@ -0,0 +1,366 @@ +/* + * $Id: ns558.c,v 1.11 2000/06/20 23:35:03 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999 Brian Gerst + * + * Sponsored by SuSE + */ + +/* + * NS558 based standard IBM game port driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/malloc.h> +#include <linux/isapnp.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); + +#define NS558_ISA 1 +#define NS558_PNP 2 +#define NS558_PCI 3 + +static int ns558_isa_portlist[] = { 0x201, 0x202, 0x203, 0x204, 0x205, 0x207, 0x209, + 0x20b, 0x20c, 0x20e, 0x20f, 0x211, 0x219, 0x101, 0 }; + +struct ns558 { + int type; + struct pci_dev *dev; + struct ns558 *next; + struct gameport gameport; +}; + +static struct ns558 *ns558 = NULL; + +/* + * ns558_isa_probe() tries to find an isa gameport at the + * specified address, and also checks for mirrors. + * A joystick must be attached for this to work. + */ + +static struct ns558* ns558_isa_probe(int io, struct ns558 *next) +{ + int i, j, b; + unsigned char c, u, v; + struct ns558 *port; + +/* + * No one should be using this address. + */ + + if (check_region(io, 1)) + return next; + +/* + * We must not be able to write arbitrary values to the port. + * The lower two axis bits must be 1 after a write. + */ + + c = inb(io); + outb(~c & ~3, io); + if (~(u = v = inb(io)) & 3) { + outb(c, io); + return next; + } +/* + * After a trigger, there must be at least some bits changing. + */ + + for (i = 0; i < 1000; i++) v &= inb(io); + + if (u == v) { + outb(c, io); + return next; + } + wait_ms(3); +/* + * After some time (4ms) the axes shouldn't change anymore. + */ + + u = inb(io); + for (i = 0; i < 1000; i++) + if ((u ^ inb(io)) & 0xf) { + outb(c, io); + return next; + } +/* + * And now find the number of mirrors of the port. + */ + + for (i = 1; i < 5; i++) { + + if (check_region(io & (-1 << i), (1 << i))) /* Don't disturb anyone */ + break; + + outb(0xff, io & (-1 << i)); + for (j = b = 0; j < 1000; j++) + if (inb(io & (-1 << i)) != inb((io & (-1 << i)) + (1 << i) - 1)) b++; + wait_ms(3); + + if (b > 300) /* We allow 30% difference */ + break; + } + + i--; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + return next; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = next; + port->type = NS558_ISA; + port->gameport.io = io; + port->gameport.size = (1 << i); + + request_region(port->gameport.io, port->gameport.size, "ns558-isa"); + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 ISA at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return port; +} + +#ifdef CONFIG_PCI +static struct pci_device_id ns558_pci_tbl[] __devinitdata = { + { 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* SB Live! gameport */ + { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, /* ESS Solo 1 */ + { 0x5333, 0xca00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, /* S3 SonicVibes */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ns558_pci_tbl); + +static int __devinit ns558_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ioport, iolen; + int rc; + struct ns558 *port; + + rc = pci_enable_device(pdev); + if (rc) { + printk(KERN_ERR "ns558: Cannot enable PCI gameport (bus %d, devfn %d) error=%d\n", + pdev->bus->number, pdev->devfn, rc); + return rc; + } + + ioport = pci_resource_start(pdev, ent->driver_data); + iolen = pci_resource_len(pdev, ent->driver_data); + + if (!request_region(ioport, iolen, "ns558-pci")) + return -EBUSY; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + return -ENOMEM; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = ns558; + port->type = NS558_PCI; + port->gameport.io = ioport; + port->gameport.size = iolen; + port->dev = pdev; + ns558 = port; + + pdev->driver_data = port; + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 PCI at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return 0; +} + +static void __devexit ns558_pci_remove(struct pci_dev *pdev) +{ + struct ns558 *port = (struct ns558 *)pdev->driver_data; + release_region(port->gameport.io, port->gameport.size); +} + +static struct pci_driver ns558_pci_driver = { + name: "PCI Gameport", + id_table: ns558_pci_tbl, + probe: ns558_pci_probe, + remove: ns558_pci_remove, +}; +#endif /* CONFIG_PCI */ + + +#ifdef CONFIG_ISAPNP +/* + * PnP IDs: + * + * CTL00c1 - SB AWE32 PnP + * CTL00c3 - SB AWE64 PnP + * CTL00f0 - SB16 PnP / Vibra 16x + * CTL7001 - SB Vibra16C PnP + * CSC0b35 - Crystal ** doesn't have compatibility ID ** + * TER1141 - Terratec AD1818 + * YMM0800 - Yamaha OPL3-SA3 + * + * PNPb02f - Generic gameport + */ + +static struct pnp_devid { + unsigned int vendor, device; +} pnp_devids[] = { + { ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x7002) }, + { ISAPNP_VENDOR('C','S','C'), ISAPNP_DEVICE(0x0b35) }, + { ISAPNP_VENDOR('P','N','P'), ISAPNP_DEVICE(0xb02f) }, + { 0, }, +}; + +static struct ns558* ns558_pnp_probe(struct pci_dev *dev, struct ns558 *next) +{ + int ioport, iolen; + struct ns558 *port; + + if (dev->prepare && dev->prepare(dev) < 0) + return next; + + if (!(dev->resource[0].flags & IORESOURCE_IO)) { + printk(KERN_WARNING "No i/o ports on a gameport? Weird\n"); + return next; + } + + if (dev->activate && dev->activate(dev) < 0) { + printk(KERN_ERR "PnP resource allocation failed\n"); + return next; + } + + ioport = pci_resource_start(dev, 0); + iolen = pci_resource_len(dev, 0); + + if (!request_region(ioport, iolen, "ns558-pnp")) + goto deactivate; + + if (!(port = kmalloc(sizeof(struct ns558), GFP_KERNEL))) { + printk(KERN_ERR "Memory allocation failed.\n"); + goto deactivate; + } + memset(port, 0, sizeof(struct ns558)); + + port->next = next; + port->type = NS558_PNP; + port->gameport.io = ioport; + port->gameport.size = iolen; + port->dev = dev; + + gameport_register_port(&port->gameport); + + printk(KERN_INFO "gameport%d: NS558 PnP at %#x", port->gameport.number, port->gameport.io); + if (port->gameport.size > 1) printk(" size %d", port->gameport.size); + printk(" speed %d kHz\n", port->gameport.speed); + + return port; + +deactivate: + if (dev->deactivate) + dev->deactivate(dev); + return next; +} +#endif + +int __init ns558_init(void) +{ + int i = 0; +#ifdef CONFIG_ISAPNP + struct pci_dev *dev = NULL; + struct pnp_devid *devid; +#endif + +/* + * Probe for ISA ports. + */ + + while (ns558_isa_portlist[i]) + ns558 = ns558_isa_probe(ns558_isa_portlist[i++], ns558); + +/* + * Probe for PCI ports. + */ +#ifdef CONFIG_PCI + pci_register_driver(&ns558_pci_driver); +#endif + +/* + * Probe for PnP ports. + */ + +#ifdef CONFIG_ISAPNP + for (devid = pnp_devids; devid->vendor; devid++) { + while ((dev = isapnp_find_dev(NULL, devid->vendor, devid->device, dev))) { + ns558 = ns558_pnp_probe(dev, ns558); + } + } +#endif + + return -!ns558; +} + +void __exit ns558_exit(void) +{ + struct ns558 *port = ns558; + + while (port) { + gameport_unregister_port(&port->gameport); + switch (port->type) { + +#ifdef CONFIG_ISAPNP + case NS558_PNP: + if (port->dev->deactivate) + port->dev->deactivate(port->dev); + /* fall through */ +#endif + + case NS558_ISA: + release_region(port->gameport.io, port->gameport.size); + break; + + default: + break; + } + + port = port->next; + } + +#ifdef CONFIG_PCI + pci_unregister_driver(&ns558_pci_driver); +#endif +} + +module_init(ns558_init); +module_exit(ns558_exit); diff --git a/drivers/char/joystick/pcigame.c b/drivers/char/joystick/pcigame.c new file mode 100644 index 000000000..0348ba08b --- /dev/null +++ b/drivers/char/joystick/pcigame.c @@ -0,0 +1,198 @@ +/* + * $Id: pcigame.c,v 1.6 2000/05/25 12:05:24 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * Based on the work of: + * Raymond Ingles + * + * Sponsored by SuSE + */ + +/* + * Trident 4DWave and Aureal Vortex gameport driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/io.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/init.h> +#include <linux/gameport.h> + +#define PCI_VENDOR_ID_AUREAL 0x12eb + +#define PCIGAME_DATA_WAIT 20 /* 20 ms */ + +#define PCIGAME_4DWAVE 0 +#define PCIGAME_VORTEX 1 +#define PCIGAME_VORTEX2 2 + +struct pcigame_data { + int gcr; /* Gameport control register */ + int legacy; /* Legacy port location */ + int axes; /* Axes start */ + int axsize; /* Axis field size */ + int axmax; /* Axis field max value */ + int adcmode; /* Value to enable ADC mode in GCR */ +}; + +static struct pcigame_data pcigame_data[] __devinitdata = +{{ 0x00030, 0x00031, 0x00034, 2, 0xffff, 0x80 }, + { 0x1100c, 0x11008, 0x11010, 4, 0x1fff, 0x40 }, + { 0x2880c, 0x28808, 0x28810, 4, 0x1fff, 0x40 }, + { 0 }}; + +struct pcigame { + struct gameport gameport; + struct pci_dev *dev; + unsigned char *base; + struct pcigame_data *data; +}; + +static unsigned char pcigame_read(struct gameport *gameport) +{ + struct pcigame *pcigame = gameport->driver; + return readb(pcigame->base + pcigame->data->legacy); +} + +static void pcigame_trigger(struct gameport *gameport) +{ + struct pcigame *pcigame = gameport->driver; + writeb(0xff, pcigame->base + pcigame->data->legacy); +} + +static int pcigame_cooked_read(struct gameport *gameport, int *axes, int *buttons) +{ + struct pcigame *pcigame = gameport->driver; + int i; + + *buttons = (~readb(pcigame->base + pcigame->data->legacy) >> 4) & 0xf; + + for (i = 0; i < 4; i++) { + axes[i] = readw(pcigame->base + pcigame->data->axes + i * pcigame->data->axsize); + if (axes[i] == pcigame->data->axmax) axes[i] = -1; + } + + return 0; +} + +static int pcigame_open(struct gameport *gameport, int mode) +{ + struct pcigame *pcigame = gameport->driver; + + switch (mode) { + case GAMEPORT_MODE_COOKED: + writeb(pcigame->data->adcmode, pcigame->base + pcigame->data->gcr); + wait_ms(PCIGAME_DATA_WAIT); + return 0; + case GAMEPORT_MODE_RAW: + writeb(0, pcigame->base + pcigame->data->gcr); + return 0; + default: + return -1; + } + + return 0; +} + +static int __devinit pcigame_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct pcigame *pcigame; + int i; + + if (!(pcigame = kmalloc(sizeof(struct pcigame), GFP_KERNEL))) + return -1; + memset(pcigame, 0, sizeof(struct pcigame)); + + + pcigame->data = pcigame_data + id->driver_data; + + pcigame->dev = dev; + dev->driver_data = pcigame; + + pcigame->gameport.driver = pcigame; + pcigame->gameport.type = GAMEPORT_EXT; + pcigame->gameport.fuzz = 64; + + pcigame->gameport.read = pcigame_read; + pcigame->gameport.trigger = pcigame_trigger; + pcigame->gameport.cooked_read = pcigame_cooked_read; + pcigame->gameport.open = pcigame_open; + + for (i = 0; i < 6; i++) + if (~pci_resource_flags(dev, i) & IORESOURCE_IO) + break; + + pci_enable_device(dev); + + pcigame->base = ioremap(pci_resource_start(pcigame->dev, i), + pci_resource_len(pcigame->dev, i)); + + gameport_register_port(&pcigame->gameport); + + printk(KERN_INFO "gameport%d: %s at pci%02x:%02x.%x speed %d kHz\n", + pcigame->gameport.number, dev->name, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), pcigame->gameport.speed); + + return 0; +} + +static void __devexit pcigame_remove(struct pci_dev *dev) +{ + struct pcigame *pcigame = dev->driver_data; + gameport_unregister_port(&pcigame->gameport); + iounmap(pcigame->base); + kfree(pcigame); +} + +static struct pci_device_id pcigame_id_table[] __devinitdata = +{{ PCI_VENDOR_ID_TRIDENT, 0x2000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, + { PCI_VENDOR_ID_TRIDENT, 0x2001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_4DWAVE }, + { PCI_VENDOR_ID_AUREAL, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX }, + { PCI_VENDOR_ID_AUREAL, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCIGAME_VORTEX2 }, + { 0 }}; + +static struct pci_driver pcigame_driver = { + name: "pcigame", + id_table: pcigame_id_table, + probe: pcigame_probe, + remove: pcigame_remove, +}; + +int __init pcigame_init(void) +{ + return pci_module_init(&pcigame_driver); +} + +void __exit pcigame_exit(void) +{ + pci_unregister_driver(&pcigame_driver); +} + +module_init(pcigame_init); +module_exit(pcigame_exit); diff --git a/drivers/char/joystick/serio.c b/drivers/char/joystick/serio.c new file mode 100644 index 000000000..9595f0a6c --- /dev/null +++ b/drivers/char/joystick/serio.c @@ -0,0 +1,132 @@ +/* + * $Id: serio.c,v 1.5 2000/06/04 17:44:59 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * The Serio abstraction module + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/serio.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); + +EXPORT_SYMBOL(serio_register_port); +EXPORT_SYMBOL(serio_unregister_port); +EXPORT_SYMBOL(serio_register_device); +EXPORT_SYMBOL(serio_unregister_device); +EXPORT_SYMBOL(serio_open); +EXPORT_SYMBOL(serio_close); +EXPORT_SYMBOL(serio_rescan); + +static struct serio *serio_list = NULL; +static struct serio_dev *serio_dev = NULL; +static int serio_number = 0; + +static void serio_find_dev(struct serio *serio) +{ + struct serio_dev *dev = serio_dev; + + while (dev && !serio->dev) { + if (dev->connect) + dev->connect(serio, dev); + dev = dev->next; + } +} + +void serio_rescan(struct serio *serio) +{ + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); + serio_find_dev(serio); +} + +void serio_register_port(struct serio *serio) +{ + serio->number = serio_number++; + serio->next = serio_list; + serio_list = serio; + serio_find_dev(serio); +} + +void serio_unregister_port(struct serio *serio) +{ + struct serio **serioptr = &serio_list; + + while (*serioptr && (*serioptr != serio)) serioptr = &((*serioptr)->next); + *serioptr = (*serioptr)->next; + + if (serio->dev && serio->dev->disconnect) + serio->dev->disconnect(serio); + + serio_number--; +} + +void serio_register_device(struct serio_dev *dev) +{ + struct serio *serio = serio_list; + + dev->next = serio_dev; + serio_dev = dev; + + while (serio) { + if (!serio->dev && dev->connect) + dev->connect(serio, dev); + serio = serio->next; + } +} + +void serio_unregister_device(struct serio_dev *dev) +{ + struct serio_dev **devptr = &serio_dev; + struct serio *serio = serio_list; + + while (*devptr && (*devptr != dev)) devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + while (serio) { + if (serio->dev == dev && dev->disconnect) + dev->disconnect(serio); + serio_find_dev(serio); + serio = serio->next; + } +} + +int serio_open(struct serio *serio, struct serio_dev *dev) +{ + if (serio->open(serio)) + return -1; + serio->dev = dev; + return 0; +} + +void serio_close(struct serio *serio) +{ + serio->close(serio); + serio->dev = NULL; +} diff --git a/drivers/char/joystick/serport.c b/drivers/char/joystick/serport.c new file mode 100644 index 000000000..453e674d7 --- /dev/null +++ b/drivers/char/joystick/serport.c @@ -0,0 +1,220 @@ +/* + * $Id: serport.c,v 1.4 2000/05/29 10:54:53 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This is a module that converts a tty line into a much simpler + * 'serial io port' abstraction that the input device drivers use. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <asm/uaccess.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/serio.h> +#include <linux/tty.h> + +struct serport { + struct tty_struct *tty; + wait_queue_head_t wait; + struct serio serio; +}; + +/* + * Callback functions from the serio code. + */ + +static int serport_serio_write(struct serio *serio, unsigned char data) +{ + struct serport *serport = serio->driver; + return -(serport->tty->driver.write(serport->tty, 0, &data, 1) != 1); +} + +static int serport_serio_open(struct serio *serio) +{ + return 0; +} + +static void serport_serio_close(struct serio *serio) +{ + struct serport *serport = serio->driver; + wake_up_interruptible(&serport->wait); +} + +/* + * serport_ldisc_open() is the routine that is called upon setting our line + * discipline on a tty. It looks for the Mag, and if found, registers + * it as a joystick device. + */ + +static int serport_ldisc_open(struct tty_struct *tty) +{ + struct serport *serport; + + MOD_INC_USE_COUNT; + + if (!(serport = kmalloc(sizeof(struct serport), GFP_KERNEL))) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + memset(serport, 0, sizeof(struct serport)); + + serport->tty = tty; + tty->disc_data = serport; + + serport->serio.type = SERIO_RS232; + serport->serio.write = serport_serio_write; + serport->serio.open = serport_serio_open; + serport->serio.close = serport_serio_close; + serport->serio.driver = serport; + + init_waitqueue_head(&serport->wait); + + return 0; +} + +/* + * serport_ldisc_close() is the opposite of serport_ldisc_open() + */ + +static void serport_ldisc_close(struct tty_struct *tty) +{ + struct serport *serport = (struct serport*) tty->disc_data; + kfree(serport); + MOD_DEC_USE_COUNT; +} + +/* + * serport_ldisc_receive() is called by the low level tty driver when characters + * are ready for us. We forward the characters, one by one to the 'interrupt' + * routine. + */ + +static void serport_ldisc_receive(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct serport *serport = (struct serport*) tty->disc_data; + int i; + for (i = 0; i < count; i++) + if (serport->serio.dev) + serport->serio.dev->interrupt(&serport->serio, cp[i], 0); +} + +/* + * serport_ldisc_room() reports how much room we do have for receiving data. + * Although we in fact have infinite room, we need to specify some value + * here, and 256 seems to be reasonable. + */ + +static int serport_ldisc_room(struct tty_struct *tty) +{ + return 256; +} + +/* + * serport_ldisc_read() just waits indefinitely if everything goes well. + * However, when the serio driver closes the serio port, it finishes, + * returning 0 characters. + */ + +static ssize_t serport_ldisc_read(struct tty_struct * tty, struct file * file, unsigned char * buf, size_t nr) +{ + struct serport *serport = (struct serport*) tty->disc_data; + DECLARE_WAITQUEUE(wait, current); + char name[32]; + + sprintf(name, tty->driver.name, MINOR(tty->device) - tty->driver.minor_start); + + serio_register_port(&serport->serio); + + printk(KERN_INFO "serio%d: Serial port %s\n", serport->serio.number, name); + + add_wait_queue(&serport->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while(serport->serio.type && !signal_pending(current)) schedule(); + + current->state = TASK_RUNNING; + remove_wait_queue(&serport->wait, &wait); + + serio_unregister_port(&serport->serio); + + return 0; +} + +/* + * serport_ldisc_ioctl() allows to set the port protocol, and device ID + */ + +static int serport_ldisc_ioctl(struct tty_struct * tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct serport *serport = (struct serport*) tty->disc_data; + + switch (cmd) { + case SPIOCSTYPE: + return get_user(serport->serio.type, (unsigned long *) arg); + } + + return -EINVAL; +} + +/* + * The line discipline structure. + */ + +static struct tty_ldisc serport_ldisc = { + name: "input", + open: serport_ldisc_open, + close: serport_ldisc_close, + read: serport_ldisc_read, + ioctl: serport_ldisc_ioctl, + receive_buf: serport_ldisc_receive, + receive_room: serport_ldisc_room, +}; + +/* + * The functions for insering/removing us as a module. + */ + +int __init serport_init(void) +{ + if (tty_register_ldisc(N_MOUSE, &serport_ldisc)) { + printk(KERN_ERR "serport.c: Error registering line discipline.\n"); + return -ENODEV; + } + + return 0; +} + +void __exit serport_exit(void) +{ + tty_register_ldisc(N_MOUSE, NULL); +} + +module_init(serport_init); +module_exit(serport_exit); diff --git a/drivers/char/joystick/sidewinder.c b/drivers/char/joystick/sidewinder.c new file mode 100644 index 000000000..861966b4e --- /dev/null +++ b/drivers/char/joystick/sidewinder.c @@ -0,0 +1,756 @@ +/* + * $Id: sidewinder.c,v 1.14 2000/05/29 11:27:55 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Microsoft SideWinder joystick family driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/gameport.h> + +/* + * These are really magic values. Changing them can make a problem go away, + * as well as break everything. + */ + +#undef SW_DEBUG + +#define SW_START 400 /* The time we wait for the first bit [400 us] */ +#define SW_STROBE 45 /* Max time per bit [45 us] */ +#define SW_TIMEOUT 4000 /* Wait for everything to settle [4 ms] */ +#define SW_KICK 45 /* Wait after A0 fall till kick [45 us] */ +#define SW_END 8 /* Number of bits before end of packet to kick */ +#define SW_FAIL 16 /* Number of packet read errors to fail and reinitialize */ +#define SW_BAD 2 /* Number of packet read errors to switch off 3d Pro optimization */ +#define SW_OK 64 /* Number of packet read successes to switch optimization back on */ +#define SW_LENGTH 512 /* Max number of bits in a packet */ +#define SW_REFRESH HZ/50 /* Time to wait between updates of joystick data [20 ms] */ + +#ifdef SW_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif + +/* + * SideWinder joystick types ... + */ + +#define SW_ID_3DP 0 +#define SW_ID_GP 1 +#define SW_ID_PP 2 +#define SW_ID_FFP 3 +#define SW_ID_FSP 4 +#define SW_ID_FFW 5 + +/* + * Names, buttons, axes ... + */ + +static char *sw_name[] = { "3D Pro", "GamePad", "Precision Pro", "Force Feedback Pro", "FreeStyle Pro", + "Force Feedback Wheel" }; + +static char sw_abs[][7] = { + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_RZ, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_X, ABS_Y, ABS_THROTTLE, ABS_HAT0X, ABS_HAT0Y }, + { ABS_RX, ABS_RUDDER, ABS_THROTTLE }}; + +static char sw_bit[][7] = { + { 10, 10, 9, 10, 1, 1 }, + { 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 7, 1, 1 }, + { 10, 10, 6, 1, 1 }, + { 10, 7, 7, 1, 1 }}; + +static short sw_btn[][12] = { + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_MODE }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_BASE3, BTN_BASE4, BTN_SELECT }, + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_START, BTN_MODE, BTN_SELECT }, + { BTN_TRIGGER, BTN_TOP, BTN_THUMB, BTN_THUMB2, BTN_BASE, BTN_BASE2, BTN_BASE3 }}; + +static struct { + int x; + int y; +} sw_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct sw { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[4]; + char name[64]; + int length; + int type; + int bits; + int number; + int fail; + int ok; + int reads; + int bads; + int used; +}; + +/* + * sw_read_packet() is a function which reads either a data packet, or an + * identification packet from a SideWinder joystick. The protocol is very, + * very, very braindamaged. Microsoft patented it in US patent #5628686. + */ + +static int sw_read_packet(struct gameport *gameport, unsigned char *buf, int length, int id) +{ + unsigned long flags; + int timeout, bitout, sched, i, kick, start, strobe; + unsigned char pending, u, v; + + i = -id; /* Don't care about data, only want ID */ + timeout = id ? gameport_time(gameport, SW_TIMEOUT) : 0; /* Set up global timeout for ID packet */ + kick = id ? gameport_time(gameport, SW_KICK) : 0; /* Set up kick timeout for ID packet */ + start = gameport_time(gameport, SW_START); + strobe = gameport_time(gameport, SW_STROBE); + bitout = start; + pending = 0; + sched = 0; + + __save_flags(flags); /* Quiet, please */ + __cli(); + + gameport_trigger(gameport); /* Trigger */ + v = gameport_read(gameport); + + do { + bitout--; + u = v; + v = gameport_read(gameport); + } while (!(~v & u & 0x10) && (bitout > 0)); /* Wait for first falling edge on clock */ + + if (bitout > 0) bitout = strobe; /* Extend time if not timed out */ + + while ((timeout > 0 || bitout > 0) && (i < length)) { + + timeout--; + bitout--; /* Decrement timers */ + sched--; + + u = v; + v = gameport_read(gameport); + + if ((~u & v & 0x10) && (bitout > 0)) { /* Rising edge on clock - data bit */ + if (i >= 0) /* Want this data */ + buf[i] = v >> 5; /* Store it */ + i++; /* Advance index */ + bitout = strobe; /* Extend timeout for next bit */ + } + + if (kick && (~v & u & 0x01)) { /* Falling edge on axis 0 */ + sched = kick; /* Schedule second trigger */ + kick = 0; /* Don't schedule next time on falling edge */ + pending = 1; /* Mark schedule */ + } + + if (pending && sched < 0 && (i > -SW_END)) { /* Second trigger time */ + gameport_trigger(gameport); /* Trigger */ + bitout = start; /* Long bit timeout */ + pending = 0; /* Unmark schedule */ + timeout = 0; /* Switch from global to bit timeouts */ + } + } + + __restore_flags(flags); /* Done - relax */ + +#ifdef SW_DEBUG + { + int j; + printk(KERN_DEBUG "sidewinder.c: Read %d triplets. [", i); + for (j = 0; j < i; j++) printk("%d", buf[j]); + printk("]\n"); + } +#endif + + return i; +} + +/* + * sw_get_bits() and GB() compose bits from the triplet buffer into a __u64. + * Parameter 'pos' is bit number inside packet where to start at, 'num' is number + * of bits to be read, 'shift' is offset in the resulting __u64 to start at, bits + * is number of bits per triplet. + */ + +#define GB(pos,num) sw_get_bits(buf, pos, num, sw->bits) + +static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits) +{ + __u64 data = 0; + int tri = pos % bits; /* Start position */ + int i = pos / bits; + int bit = 0; + + while (num--) { + data |= (__u64)((buf[i] >> tri++) & 1) << bit++; /* Transfer bit */ + if (tri == bits) { + i++; /* Next triplet */ + tri = 0; + } + } + + return data; +} + +/* + * sw_init_digital() initializes a SideWinder 3D Pro joystick + * into digital mode. + */ + +static void sw_init_digital(struct gameport *gameport) +{ + int seq[] = { 140, 140+725, 140+300, 0 }; + unsigned long flags; + int i, t; + + __save_flags(flags); + __cli(); + + i = 0; + do { + gameport_trigger(gameport); /* Trigger */ + t = gameport_time(gameport, SW_TIMEOUT); + while ((gameport_read(gameport) & 1) && t) t--; /* Wait for axis to fall back to 0 */ + udelay(seq[i]); /* Delay magic time */ + } while (seq[++i]); + + gameport_trigger(gameport); /* Last trigger */ + + __restore_flags(flags); +} + +/* + * sw_parity() computes parity of __u64 + */ + +static int sw_parity(__u64 t) +{ + int x = t ^ (t >> 32); + x ^= x >> 16; + x ^= x >> 8; + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1; +} + +/* + * sw_ccheck() checks synchronization bits and computes checksum of nibbles. + */ + +static int sw_check(__u64 t) +{ + unsigned char sum = 0; + + if ((t & 0x8080808080808080ULL) ^ 0x80) /* Sync */ + return -1; + + while (t) { /* Sum */ + sum += t & 0xf; + t >>= 4; + } + + return sum & 0xf; +} + +/* + * sw_parse() analyzes SideWinder joystick data, and writes the results into + * the axes and buttons arrays. + */ + +static int sw_parse(unsigned char *buf, struct sw *sw) +{ + int hat, i, j; + struct input_dev *dev = sw->dev; + + switch (sw->type) { + + case SW_ID_3DP: + + if (sw_check(GB(0,64)) || (hat = (GB(6,1) << 3) | GB(60,3)) > 8) return -1; + + input_report_abs(dev, ABS_X, (GB( 3,3) << 7) | GB(16,7)); + input_report_abs(dev, ABS_Y, (GB( 0,3) << 7) | GB(24,7)); + input_report_abs(dev, ABS_RZ, (GB(35,2) << 7) | GB(40,7)); + input_report_abs(dev, ABS_THROTTLE, (GB(32,3) << 7) | GB(48,7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 7; j++) + input_report_key(dev, sw_btn[SW_ID_3DP][j], !GB(j+8,1)); + + input_report_key(dev, BTN_BASE4, !GB(38,1)); + input_report_key(dev, BTN_BASE5, !GB(37,1)); + + return 0; + + case SW_ID_GP: + + for (i = 0; i < sw->number; i ++) { + + if (sw_parity(GB(i*15,15))) return -1; + + input_report_key(dev + i, ABS_X, GB(i*15+3,1) - GB(i*15+2,1)); + input_report_key(dev + i, ABS_Y, GB(i*15+0,1) - GB(i*15+1,1)); + + for (j = 0; j < 10; j++) + input_report_key(dev, sw_btn[SW_ID_GP][j], !GB(i*15+j+4,1)); + } + + return 0; + + case SW_ID_PP: + case SW_ID_FFP: + + if (!sw_parity(GB(0,48)) || (hat = GB(42,4)) > 8) return -1; + + input_report_abs(dev, ABS_X, GB( 9,10)); + input_report_abs(dev, ABS_Y, GB(19,10)); + input_report_abs(dev, ABS_RZ, GB(36, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(29, 7)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 9; j++) + input_report_key(dev, sw_btn[SW_ID_PP][j], !GB(j,1)); + + return 0; + + case SW_ID_FSP: + + if (!sw_parity(GB(0,43)) || (hat = GB(28,4)) > 8) return -1; + + input_report_abs(dev, ABS_X, GB( 0,10)); + input_report_abs(dev, ABS_Y, GB(16,10)); + input_report_abs(dev, ABS_THROTTLE, GB(32, 6)); + + input_report_abs(dev, ABS_HAT0X, sw_hat_to_axis[hat].x); + input_report_abs(dev, ABS_HAT0Y, sw_hat_to_axis[hat].y); + + for (j = 0; j < 6; j++) + input_report_key(dev, sw_btn[SW_ID_FSP][j], !GB(j+10,1)); + + input_report_key(dev, BTN_TR, GB(26,1)); + input_report_key(dev, BTN_START, GB(27,1)); + input_report_key(dev, BTN_MODE, GB(38,1)); + input_report_key(dev, BTN_SELECT, GB(39,1)); + + return 0; + + case SW_ID_FFW: + + if (!sw_parity(GB(0,33))) return -1; + + input_report_abs(dev, ABS_RX, GB( 0,10)); + input_report_abs(dev, ABS_RUDDER, GB(10, 6)); + input_report_abs(dev, ABS_THROTTLE, GB(16, 6)); + + for (j = 0; j < 8; j++) + input_report_key(dev, sw_btn[SW_ID_FFW][j], !GB(j+22,1)); + + return 0; + } + + return -1; +} + +/* + * sw_read() reads SideWinder joystick data, and reinitializes + * the joystick in case of persistent problems. This is the function that is + * called from the generic code to poll the joystick. + */ + +static int sw_read(struct sw *sw) +{ + unsigned char buf[SW_LENGTH]; + int i; + + i = sw_read_packet(sw->gameport, buf, sw->length, 0); + + if (sw->type == SW_ID_3DP && sw->length == 66 && i != 66) { /* Broken packet, try to fix */ + + if (i == 64 && !sw_check(sw_get_bits(buf,0,64,1))) { /* Last init failed, 1 bit mode */ + printk(KERN_WARNING "sidewinder.c: Joystick in wrong mode on gameport%d" + " - going to reinitialize.\n", sw->gameport->number); + sw->fail = SW_FAIL; /* Reinitialize */ + i = 128; /* Bogus value */ + } + + if (i < 66 && GB(0,64) == GB(i*3-66,64)) /* 1 == 3 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(0,64) == GB(66,64)) /* 1 == 2 */ + i = 66; /* Everything is fine */ + + if (i < 66 && GB(i*3-132,64) == GB(i*3-66,64)) { /* 2 == 3 */ + memmove(buf, buf + i - 22, 22); /* Move data */ + i = 66; /* Carry on */ + } + } + + if (i == sw->length && !sw_parse(buf, sw)) { /* Parse data */ + + sw->fail = 0; + sw->ok++; + + if (sw->type == SW_ID_3DP && sw->length == 66 /* Many packets OK */ + && sw->ok > SW_OK) { + + printk(KERN_INFO "sidewinder.c: No more trouble on gameport%d" + " - enabling optimization again.\n", sw->gameport->number); + sw->length = 22; + } + + return 0; + } + + sw->ok = 0; + sw->fail++; + + if (sw->type == SW_ID_3DP && sw->length == 22 && sw->fail > SW_BAD) { /* Consecutive bad packets */ + + printk(KERN_INFO "sidewinder.c: Many bit errors on gameport%d" + " - disabling optimization.\n", sw->gameport->number); + sw->length = 66; + } + + if (sw->fail < SW_FAIL) return -1; /* Not enough, don't reinitialize yet */ + + printk(KERN_WARNING "sidewinder.c: Too many bit errors on gameport%d" + " - reinitializing joystick.\n", sw->gameport->number); + + if (!i && sw->type == SW_ID_3DP) { /* 3D Pro can be in analog mode */ + udelay(3 * SW_TIMEOUT); + sw_init_digital(sw->gameport); + } + + udelay(SW_TIMEOUT); + i = sw_read_packet(sw->gameport, buf, SW_LENGTH, 0); /* Read normal data packet */ + udelay(SW_TIMEOUT); + sw_read_packet(sw->gameport, buf, SW_LENGTH, i); /* Read ID packet, this initializes the stick */ + + sw->fail = SW_FAIL; + + return -1; +} + +static void sw_timer(unsigned long private) +{ + struct sw *sw = (void *) private; + + sw->reads++; + if (sw_read(sw)) sw->bads++; + mod_timer(&sw->timer, jiffies + SW_REFRESH); +} + +static int sw_open(struct input_dev *dev) +{ + struct sw *sw = dev->private; + if (!sw->used++) + mod_timer(&sw->timer, jiffies + SW_REFRESH); + return 0; +} + +static void sw_close(struct input_dev *dev) +{ + struct sw *sw = dev->private; + if (!--sw->used) + del_timer(&sw->timer); +} + +/* + * sw_print_packet() prints the contents of a SideWinder packet. + */ + +static void sw_print_packet(char *name, int length, unsigned char *buf, char bits) +{ + int i; + + printk(KERN_INFO "sidewinder.c: %s packet, %d bits. [", name, length); + for (i = (((length + 3) >> 2) - 1); i >= 0; i--) + printk("%x", (int)sw_get_bits(buf, i << 2, 4, bits)); + printk("]\n"); +} + +/* + * sw_3dp_id() translates the 3DP id into a human legible string. + * Unfortunately I don't know how to do this for the other SW types. + */ + +static void sw_3dp_id(unsigned char *buf, char *comment) +{ + int i; + char pnp[8], rev[9]; + + for (i = 0; i < 7; i++) /* ASCII PnP ID */ + pnp[i] = sw_get_bits(buf, 24+8*i, 8, 1); + + for (i = 0; i < 8; i++) /* ASCII firmware revision */ + rev[i] = sw_get_bits(buf, 88+8*i, 8, 1); + + pnp[7] = rev[8] = 0; + + sprintf(comment, " [PnP %d.%02d id %s rev %s]", + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */ + sw_get_bits(buf, 16, 6, 1)) / 100, + (int) ((sw_get_bits(buf, 8, 6, 1) << 6) | + sw_get_bits(buf, 16, 6, 1)) % 100, + pnp, rev); +} + +/* + * sw_guess_mode() checks the upper two button bits for toggling - + * indication of that the joystick is in 3-bit mode. This is documented + * behavior for 3DP ID packet, and for example the FSP does this in + * normal packets instead. Fun ... + */ + +static int sw_guess_mode(unsigned char *buf, int len) +{ + int i; + unsigned char xor = 0; + for (i = 1; i < len; i++) xor |= (buf[i - 1] ^ buf[i]) & 6; + return !!xor * 2 + 1; +} + +/* + * sw_connect() probes for SideWinder type joysticks. + */ + +static void sw_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct sw *sw; + int i, j, k, l; + unsigned char buf[SW_LENGTH]; + unsigned char idbuf[SW_LENGTH]; + unsigned char m = 1; + char comment[40]; + + comment[0] = 0; + + if (!(sw = kmalloc(sizeof(struct sw), GFP_KERNEL))) return; + memset(sw, 0, sizeof(struct sw)); + + gameport->private = sw; + + sw->gameport = gameport; + init_timer(&sw->timer); + sw->timer.data = (long) sw; + sw->timer.function = sw_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read normal packet */ + m |= sw_guess_mode(buf, i); /* Data packet (1-bit) can carry mode info [FSP] */ + udelay(SW_TIMEOUT); + dbg("Init 1: Mode %d. Length %d.", m , i); + + if (!i) { /* No data. 3d Pro analog mode? */ + sw_init_digital(gameport); /* Switch to digital */ + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + udelay(SW_TIMEOUT); + dbg("Init 1b: Length %d.", i); + if (!i) goto fail2; /* No data -> FAIL */ + } + + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Read ID. This initializes the stick */ + m |= sw_guess_mode(idbuf, j); /* ID packet should carry mode info [3DP] */ + dbg("Init 2: Mode %d. ID Length %d.", m , j); + + if (!j) { /* Read ID failed. Happens in 1-bit mode on PP */ + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Retry reading packet */ + dbg("Init 2b: Mode %d. Length %d.", m, i); + if (!i) goto fail2; + udelay(SW_TIMEOUT); + j = sw_read_packet(gameport, idbuf, SW_LENGTH, i); /* Retry reading ID */ + dbg("Init 2c: ID Length %d.", j); + } + + sw->type = -1; + k = SW_FAIL; /* Try SW_FAIL times */ + l = 0; + + do { + k--; + udelay(SW_TIMEOUT); + i = sw_read_packet(gameport, buf, SW_LENGTH, 0); /* Read data packet */ + dbg("Init 3: Mode %d. Length %d. Last %d. Tries %d.", m, i, l, k); + + if (i > l) { /* Longer? As we can only lose bits, it makes */ + /* no sense to try detection for a packet shorter */ + l = i; /* than the previous one */ + + sw->number = 1; + sw->gameport = gameport; + sw->length = i; + sw->bits = m; + + dbg("Init 3a: Case %d.\n", i * m); + + switch (i * m) { + case 60: + sw->number++; + case 45: /* Ambiguous packet length */ + if (j <= 40) { /* ID length less or eq 40 -> FSP */ + case 43: + sw->type = SW_ID_FSP; + break; + } + sw->number++; + case 30: + sw->number++; + case 15: + sw->type = SW_ID_GP; + break; + case 33: + case 31: + sw->type = SW_ID_FFW; + break; + case 48: /* Ambiguous */ + if (j == 14) { /* ID length 14*3 -> FFP */ + sw->type = SW_ID_FFP; + sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on"); + } else + sw->type = SW_ID_PP; + break; + case 198: + sw->length = 22; + case 64: + sw->type = SW_ID_3DP; + if (j == 160) sw_3dp_id(idbuf, comment); + break; + } + } + + } while (k && (sw->type == -1)); + + if (sw->type == -1) { + printk(KERN_WARNING "sidewinder.c: unknown joystick device detected " + "on gameport%d, contact <vojtech@suse.cz>\n", gameport->number); + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); + goto fail2; + } + +#ifdef SW_DEBUG + sw_print_packet("ID", j * 3, idbuf, 3); + sw_print_packet("Data", i * m, buf, m); +#endif + + k = i; + l = j; + + for (i = 0; i < sw->number; i++) { + int bits, code; + + sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]); + + sw->dev[i].private = sw; + + sw->dev[i].open = sw_open; + sw->dev[i].close = sw_close; + + sw->dev[i].name = sw->name; + sw->dev[i].idbus = BUS_GAMEPORT; + sw->dev[i].idvendor = GAMEPORT_ID_VENDOR_MICROSOFT; + sw->dev[i].idproduct = sw->type; + sw->dev[i].idversion = 0x0100; + + sw->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (j = 0; (bits = sw_bit[sw->type][j]); j++) { + code = sw_abs[sw->type][j]; + set_bit(code, sw->dev[i].absbit); + sw->dev[i].absmax[code] = (1 << bits) - 1; + sw->dev[i].absmin[code] = (bits == 1) ? -1 : 0; + sw->dev[i].absfuzz[code] = ((bits >> 1) >= 2) ? (1 << ((bits >> 1) - 2)) : 0; + if (code != ABS_THROTTLE) + sw->dev[i].absflat[code] = (bits >= 5) ? (1 << (bits - 5)) : 0; + } + + for (j = 0; (code = sw_btn[sw->type][j]); j++) + set_bit(code, sw->dev[i].keybit); + + input_register_device(sw->dev + i); + printk(KERN_INFO "input%d: %s%s on gameport%d.%d [%d-bit id %d data %d]\n", + sw->dev[i].number, sw->name, comment, gameport->number, i, m, l, k); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(sw); +} + +static void sw_disconnect(struct gameport *gameport) +{ + int i; + + struct sw *sw = gameport->private; + for (i = 0; i < sw->number; i++) + input_unregister_device(sw->dev + i); + gameport_close(gameport); + kfree(sw); +} + +static struct gameport_dev sw_dev = { + connect: sw_connect, + disconnect: sw_disconnect, +}; + +int __init sw_init(void) +{ + gameport_register_device(&sw_dev); + return 0; +} + +void __exit sw_exit(void) +{ + gameport_unregister_device(&sw_dev); +} + +module_init(sw_init); +module_exit(sw_exit); diff --git a/drivers/char/joystick/spaceball.c b/drivers/char/joystick/spaceball.c new file mode 100644 index 000000000..4f5059330 --- /dev/null +++ b/drivers/char/joystick/spaceball.c @@ -0,0 +1,231 @@ +/* + * $Id: spaceball.c,v 1.6 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + * Joseph Krahn + * + * Sponsored by SuSE + */ + +/* + * SpaceTec SpaceBall 4000 FLX driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> + +/* + * Constants. + */ + +#define JS_SBALL_MAX_LENGTH 128 +static int spaceball_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ }; +static char *spaceball_name = "SpaceTec SpaceBall 4000 FLX"; + +/* + * Per-Ball data. + */ + +struct spaceball { + struct input_dev dev; + struct serio *serio; + int idx; + int escape; + unsigned char data[JS_SBALL_MAX_LENGTH]; +}; + +/* + * spaceball_process_packet() decodes packets the driver receives from the + * SpaceBall. + */ + +static void spaceball_process_packet(struct spaceball* spaceball) +{ + struct input_dev *dev = &spaceball->dev; + unsigned char *data = spaceball->data; + int i, d; + + if (spaceball->idx < 2) return; + + switch (spaceball->data[0]) { + + case '@': /* Reset packet */ + spaceball->data[spaceball->idx - 1] = 0; + for (i = 1; i < spaceball->idx && spaceball->data[i] == ' '; i++); + printk(KERN_INFO "input%d: %s [%s] on serio%d\n", + spaceball->dev.number, spaceball_name, spaceball->data + i, spaceball->serio->number); + break; + + case 'D': /* Ball data */ + if (spaceball->idx != 16) return; + for (i = 0; i < 6; i++) { + d = ((data[2 * i + 3] << 8) | data[2 * i + 2]); + input_report_abs(dev, spaceball_axes[i], d - ((d & 0x8000) ? 0x10000 : 0)); + } + break; + + case '.': /* Button data, part2 */ + if (spaceball->idx != 4) return; + input_report_key(dev, BTN_LEFT, data[2] & 1); + input_report_key(dev, BTN_RIGHT, data[2] & 2); + break; + + case '?': /* Error packet */ + spaceball->data[spaceball->idx - 1] = 0; + printk(KERN_ERR "spaceball: Device error. [%s]\n", spaceball->data + 1); + break; + } +} + +/* + * Spaceball 4000 FLX packets all start with a one letter packet-type decriptor, + * and end in 0x0d. It uses '^' as an escape for 0x0d characters which can + * occur in the axis values. ^M, ^Q and ^S all mean 0x0d, depending (I think) + * on whether the axis value is increasing, decreasing, or same as before. + * (I don't see why this is useful). + */ + +static void spaceball_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct spaceball *spaceball = serio->private; + + switch (data) { + case 0xd: + if (spaceball->idx) + spaceball_process_packet(spaceball); + spaceball->idx = 0; + spaceball->escape = 0; + return; + case 'M': + case 'Q': + case 'S': + if (spaceball->escape) + data = 0xd; + case '^': + spaceball->escape ^= 1; + default: + if (spaceball->escape) { + printk(KERN_WARNING "spaceball.c: Unknown escaped character: %#x\n", data); + spaceball->escape = 0; + } + if (spaceball->idx < JS_SBALL_MAX_LENGTH) + spaceball->data[spaceball->idx++] = data; + return; + } +} + +/* + * spaceball_disconnect() is the opposite of spaceball_connect() + */ + +static void spaceball_disconnect(struct serio *serio) +{ + struct spaceball* spaceball = serio->private; + input_unregister_device(&spaceball->dev); + serio_close(serio); + kfree(spaceball); +} + +/* + * spaceball_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Magellan, and if found, registers + * it as an input device. + */ + +static void spaceball_connect(struct serio *serio, struct serio_dev *dev) +{ + struct spaceball *spaceball; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_SPACEBALL)) + return; + + if (!(spaceball = kmalloc(sizeof(struct spaceball), GFP_KERNEL))) + return; + memset(spaceball, 0, sizeof(struct spaceball)); + + spaceball->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + spaceball->dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); + + for (i = 0; i < 6; i++) { + t = spaceball_axes[i]; + set_bit(t, spaceball->dev.absbit); + spaceball->dev.absmin[t] = i < 3 ? -10000 : -2000; + spaceball->dev.absmax[t] = i < 3 ? 10000 : 2000; + spaceball->dev.absflat[t] = i < 3 ? 50 : 10; + spaceball->dev.absfuzz[t] = i < 3 ? 12 : 2; + } + + spaceball->serio = serio; + spaceball->dev.private = spaceball; + + spaceball->dev.name = spaceball_name; + spaceball->dev.idbus = BUS_RS232; + spaceball->dev.idvendor = SERIO_SPACEBALL; + spaceball->dev.idproduct = 0x0001; + spaceball->dev.idversion = 0x0100; + + serio->private = spaceball; + + if (serio_open(serio, dev)) { + kfree(spaceball); + return; + } + + input_register_device(&spaceball->dev); +} + +/* + * The serio device structure. + */ + +static struct serio_dev spaceball_dev = { + interrupt: spaceball_interrupt, + connect: spaceball_connect, + disconnect: spaceball_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init spaceball_init(void) +{ + serio_register_device(&spaceball_dev); + return 0; +} + +void __exit spaceball_exit(void) +{ + serio_unregister_device(&spaceball_dev); +} + +module_init(spaceball_init); +module_exit(spaceball_exit); diff --git a/drivers/char/joystick/spaceorb.c b/drivers/char/joystick/spaceorb.c new file mode 100644 index 000000000..866e1ba50 --- /dev/null +++ b/drivers/char/joystick/spaceorb.c @@ -0,0 +1,225 @@ +/* + * $Id: spaceorb.c,v 1.7 2000/05/29 11:19:51 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Based on the work of: + * David Thompson + * + * Sponsored by SuSE + */ + +/* + * SpaceTec SpaceOrb 360 and Avenger 6dof controller driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/serio.h> + +/* + * Constants. + */ + +#define SPACEORB_MAX_LENGTH 64 + +static int spaceorb_buttons[] = { BTN_TL, BTN_TR, BTN_Y, BTN_X, BTN_B, BTN_A, BTN_MODE}; +static int spaceorb_axes[] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ}; +static char *spaceorb_name = "SpaceTec SpaceOrb 360"; + +/* + * Per-Orb data. + */ + +struct spaceorb { + struct input_dev dev; + struct serio *serio; + int idx; + unsigned char data[SPACEORB_MAX_LENGTH]; +}; + +static unsigned char spaceorb_xor[] = "SpaceWare"; + +static unsigned char *spaceorb_errors[] = { "EEPROM storing 0 failed", "Receive queue overflow", "Transmit queue timeout", + "Bad packet", "Power brown-out", "EEPROM checksum error", "Hardware fault" }; + +/* + * spaceorb_process_packet() decodes packets the driver receives from the + * SpaceOrb. + */ + +static void spaceorb_process_packet(struct spaceorb *spaceorb) +{ + struct input_dev *dev = &spaceorb->dev; + unsigned char *data = spaceorb->data; + unsigned char c = 0; + int axes[6]; + int i; + + if (spaceorb->idx < 2) return; + for (i = 0; i < spaceorb->idx; i++) c ^= data[i]; + if (c) return; + + switch (data[0]) { + + case 'R': /* Reset packet */ + spaceorb->data[spaceorb->idx - 1] = 0; + for (i = 1; i < spaceorb->idx && spaceorb->data[i] == ' '; i++); + printk(KERN_INFO "input%d: %s [%s] on serio%d\n", + spaceorb->dev.number, spaceorb_name, spaceorb->data + i, spaceorb->serio->number); + break; + + case 'D': /* Ball + button data */ + if (spaceorb->idx != 12) return; + for (i = 0; i < 9; i++) spaceorb->data[i+2] ^= spaceorb_xor[i]; + axes[0] = ( data[2] << 3) | (data[ 3] >> 4); + axes[1] = ((data[3] & 0x0f) << 6) | (data[ 4] >> 1); + axes[2] = ((data[4] & 0x01) << 9) | (data[ 5] << 2) | (data[4] >> 5); + axes[3] = ((data[6] & 0x1f) << 5) | (data[ 7] >> 2); + axes[4] = ((data[7] & 0x03) << 8) | (data[ 8] << 1) | (data[7] >> 6); + axes[5] = ((data[9] & 0x3f) << 4) | (data[10] >> 3); + for (i = 0; i < 6; i++) + input_report_abs(dev, spaceorb_axes[i], axes[i] - ((axes[i] & 0x200) ? 1024 : 0)); + for (i = 0; i < 8; i++) + input_report_key(dev, spaceorb_buttons[i], (data[1] >> i) & 1); + break; + + case 'K': /* Button data */ + if (spaceorb->idx != 5) return; + for (i = 0; i < 7; i++) + input_report_key(dev, spaceorb_buttons[i], (data[2] >> i) & 1); + + break; + + case 'E': /* Error packet */ + if (spaceorb->idx != 4) return; + printk(KERN_ERR "joy-spaceorb: Device error. [ "); + for (i = 0; i < 7; i++) if (data[1] & (1 << i)) printk("%s ", spaceorb_errors[i]); + printk("]\n"); + break; + } +} + +static void spaceorb_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct spaceorb* spaceorb = serio->private; + + if (~data & 0x80) { + if (spaceorb->idx) spaceorb_process_packet(spaceorb); + spaceorb->idx = 0; + } + if (spaceorb->idx < SPACEORB_MAX_LENGTH) + spaceorb->data[spaceorb->idx++] = data & 0x7f; +} + +/* + * spaceorb_disconnect() is the opposite of spaceorb_connect() + */ + +static void spaceorb_disconnect(struct serio *serio) +{ + struct spaceorb* spaceorb = serio->private; + input_unregister_device(&spaceorb->dev); + serio_close(serio); + kfree(spaceorb); +} + +/* + * spaceorb_connect() is the routine that is called when someone adds a + * new serio device. It looks for the SpaceOrb/Avenger, and if found, registers + * it as an input device. + */ + +static void spaceorb_connect(struct serio *serio, struct serio_dev *dev) +{ + struct spaceorb *spaceorb; + int i, t; + + if (serio->type != (SERIO_RS232 | SERIO_SPACEORB)) + return; + + if (!(spaceorb = kmalloc(sizeof(struct spaceorb), GFP_KERNEL))) + return; + memset(spaceorb, 0, sizeof(struct spaceorb)); + + spaceorb->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < 7; i++) + set_bit(spaceorb_buttons[i], &spaceorb->dev.keybit); + + for (i = 0; i < 6; i++) { + t = spaceorb_axes[i]; + set_bit(t, spaceorb->dev.absbit); + spaceorb->dev.absmin[t] = -508; + spaceorb->dev.absmax[t] = 508; + } + + spaceorb->serio = serio; + spaceorb->dev.private = spaceorb; + + spaceorb->dev.name = spaceorb_name; + spaceorb->dev.idbus = BUS_RS232; + spaceorb->dev.idvendor = SERIO_SPACEORB; + spaceorb->dev.idproduct = 0x0001; + spaceorb->dev.idversion = 0x0100; + + serio->private = spaceorb; + + if (serio_open(serio, dev)) { + kfree(spaceorb); + return; + } + + input_register_device(&spaceorb->dev); +} + +/* + * The serio device structure. + */ + +static struct serio_dev spaceorb_dev = { + interrupt: spaceorb_interrupt, + connect: spaceorb_connect, + disconnect: spaceorb_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init spaceorb_init(void) +{ + serio_register_device(&spaceorb_dev); + return 0; +} + +void __exit spaceorb_exit(void) +{ + serio_unregister_device(&spaceorb_dev); +} + +module_init(spaceorb_init); +module_exit(spaceorb_exit); diff --git a/drivers/char/joystick/tmdc.c b/drivers/char/joystick/tmdc.c new file mode 100644 index 000000000..f356f7dd5 --- /dev/null +++ b/drivers/char/joystick/tmdc.c @@ -0,0 +1,348 @@ +/* + * $Id: tmdc.c,v 1.18 2000/06/08 19:59:59 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Sponsored by SuSE + * + * Based on the work of: + * Trystan Larey-Williams + * + */ + +/* + * ThrustMaster DirectConnect (BSP) joystick family driver for Linux + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/gameport.h> +#include <linux/input.h> + +#define TMDC_MAX_START 400 /* 400 us */ +#define TMDC_MAX_STROBE 45 /* 45 us */ +#define TMDC_MAX_LENGTH 13 +#define TMDC_REFRESH_TIME HZ/50 /* 20 ms */ + +#define TMDC_MODE_M3DI 1 +#define TMDC_MODE_3DRP 3 +#define TMDC_MODE_FGP 163 + +#define TMDC_BYTE_ID 10 +#define TMDC_BYTE_REV 11 +#define TMDC_BYTE_DEF 12 + +#define TMDC_ABS 7 +#define TMDC_ABS_HAT 4 +#define TMDC_BTN_PAD 10 +#define TMDC_BTN_JOY 16 + +static unsigned char tmdc_byte_a[16] = { 0, 1, 3, 4, 6, 7 }; +static unsigned char tmdc_byte_d[16] = { 2, 5, 8, 9 }; + +static unsigned char tmdc_abs[TMDC_ABS] = + { ABS_X, ABS_Y, ABS_RUDDER, ABS_THROTTLE, ABS_RX, ABS_RY, ABS_RZ }; +static unsigned char tmdc_abs_hat[TMDC_ABS_HAT] = + { ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y }; +static unsigned short tmdc_btn_pad[TMDC_BTN_PAD] = + { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_START, BTN_SELECT, BTN_TL, BTN_TR }; +static unsigned short tmdc_btn_joy[TMDC_BTN_JOY] = + { BTN_TRIGGER, BTN_THUMB, BTN_TOP, BTN_TOP2, BTN_BASE, BTN_BASE2, BTN_THUMB2, BTN_PINKIE, + BTN_BASE3, BTN_BASE4, BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z }; + +struct tmdc { + struct gameport *gameport; + struct timer_list timer; + struct input_dev dev[2]; + char name[2][64]; + int mode[2]; + int used; + int reads; + int bads; + unsigned char exists; +}; + +/* + * tmdc_read_packet() reads a ThrustMaster packet. + */ + +static int tmdc_read_packet(struct gameport *gameport, unsigned char data[2][TMDC_MAX_LENGTH]) +{ + unsigned char u, v, w, x; + unsigned long flags; + int i[2], j[2], t[2], p, k; + + p = gameport_time(gameport, TMDC_MAX_STROBE); + + for (k = 0; k < 2; k++) { + t[k] = gameport_time(gameport, TMDC_MAX_START); + i[k] = j[k] = 0; + } + + __save_flags(flags); + __cli(); + gameport_trigger(gameport); + + w = gameport_read(gameport) >> 4; + + do { + x = w; + w = gameport_read(gameport) >> 4; + + for (k = 0, v = w, u = x; k < 2; k++, v >>= 2, u >>= 2) { + if (~v & u & 2) { + if (t[k] <= 0 || i[k] >= TMDC_MAX_LENGTH) continue; + t[k] = p; + if (j[k] == 0) { /* Start bit */ + if (~v & 1) t[k] = 0; + data[k][i[k]] = 0; j[k]++; continue; + } + if (j[k] == 9) { /* Stop bit */ + if (v & 1) t[k] = 0; + j[k] = 0; i[k]++; continue; + } + data[k][i[k]] |= (~v & 1) << (j[k]++ - 1); /* Data bit */ + } + t[k]--; + } + } while (t[0] > 0 || t[1] > 0); + + __restore_flags(flags); + + return (i[0] == TMDC_MAX_LENGTH) | ((i[1] == TMDC_MAX_LENGTH) << 1); +} + +/* + * tmdc_read() reads and analyzes ThrustMaster joystick data. + */ + +static void tmdc_timer(unsigned long private) +{ + unsigned char data[2][TMDC_MAX_LENGTH]; + struct tmdc *tmdc = (void *) private; + struct input_dev *dev; + unsigned char r, bad = 0; + int i, j; + + tmdc->reads++; + + if ((r = tmdc_read_packet(tmdc->gameport, data)) != tmdc->exists) + bad = 1; + + for (j = 0; j < 2; j++) + if (r & (1 << j) & tmdc->exists) { + + if (data[j][TMDC_BYTE_ID] != tmdc->mode[j]) { + bad = 1; + continue; + } + + dev = tmdc->dev + j; + + for (i = 0; i < data[j][TMDC_BYTE_DEF] >> 4; i++) + input_report_abs(dev, tmdc_abs[i], data[j][tmdc_byte_a[i]]); + + switch (tmdc->mode[j]) { + + case TMDC_MODE_M3DI: + + i = tmdc_byte_d[0]; + + input_report_abs(dev, ABS_HAT0X, ((data[j][i] >> 3) & 1) - ((data[j][i] >> 1) & 1)); + input_report_abs(dev, ABS_HAT0Y, ((data[j][i] >> 2) & 1) - ( data[j][i] & 1)); + + for (i = 0; i < 4; i++) + input_report_key(dev, tmdc_btn_joy[i], + (data[j][tmdc_byte_d[0]] >> (i + 4)) & 1); + for (i = 0; i < 2; i++) + input_report_key(dev, tmdc_btn_joy[i + 4], + (data[j][tmdc_byte_d[1]] >> (i + 6)) & 1); + + break; + + case TMDC_MODE_3DRP: + case TMDC_MODE_FGP: + + for (i = 0; i < 10; i++) + input_report_key(dev, tmdc_btn_pad[i], + (data[j][tmdc_byte_d[i >> 3]] >> (i & 7)) & 1); + + break; + + default: + + for (i = 0; i < ((data[j][TMDC_BYTE_DEF] & 0xf) << 3) && i < TMDC_BTN_JOY; i++) + input_report_key(dev, tmdc_btn_joy[i], + (data[j][tmdc_byte_d[i >> 3]] >> (i & 7)) & 1); + + break; + + } + } + + tmdc->bads += bad; + + mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME); +} + +static int tmdc_open(struct input_dev *dev) +{ + struct tmdc *tmdc = dev->private; + if (!tmdc->used++) + mod_timer(&tmdc->timer, jiffies + TMDC_REFRESH_TIME); + return 0; +} + +static void tmdc_close(struct input_dev *dev) +{ + struct tmdc *tmdc = dev->private; + if (!--tmdc->used) + del_timer(&tmdc->timer); +} + +/* + * tmdc_probe() probes for ThrustMaster type joysticks. + */ + +static void tmdc_connect(struct gameport *gameport, struct gameport_dev *dev) +{ + struct tmdc *tmdc; + struct js_tm_models { + unsigned char id; + char *name; + char abs; + char hats; + char joybtn; + char padbtn; + } models[] = { { 1, "ThrustMaster Millenium 3D Inceptor", 6, 0, 6, 0 }, + { 3, "ThrustMaster Rage 3D Gamepad", 2, 0, 0, 10 }, + { 163, "Thrustmaster Fusion GamePad", 2, 0, 0, 10 }, + { 0, "Unknown %d-axis, %d-button TM device %d", 0, 0, 0, 0 }}; + unsigned char data[2][TMDC_MAX_LENGTH]; + int i, j, m; + + if (!(tmdc = kmalloc(sizeof(struct tmdc), GFP_KERNEL))) + return; + memset(tmdc, 0, sizeof(struct tmdc)); + + gameport->private = tmdc; + + tmdc->gameport = gameport; + init_timer(&tmdc->timer); + tmdc->timer.data = (long) tmdc; + tmdc->timer.function = tmdc_timer; + + if (gameport_open(gameport, dev, GAMEPORT_MODE_RAW)) + goto fail1; + + if (!(tmdc->exists = tmdc_read_packet(gameport, data))) + goto fail2; + + for (j = 0; j < 2; j++) + if (tmdc->exists & (1 << j)) { + + tmdc->mode[j] = data[j][TMDC_BYTE_ID]; + + for (m = 0; models[m].id && models[m].id != tmdc->mode[j]; m++); + + if (!models[m].id) { + models[m].abs = data[j][TMDC_BYTE_DEF] >> 4; + models[m].joybtn = (data[j][TMDC_BYTE_DEF] & 0xf) << 3; + } + + sprintf(tmdc->name[j], models[m].name, models[m].abs, models[m].joybtn, tmdc->mode[j]); + + tmdc->dev[j].private = tmdc; + tmdc->dev[j].open = tmdc_open; + tmdc->dev[j].close = tmdc_close; + + tmdc->dev[j].name = tmdc->name[j]; + tmdc->dev[j].idbus = BUS_GAMEPORT; + tmdc->dev[j].idvendor = GAMEPORT_ID_VENDOR_THRUSTMASTER; + tmdc->dev[j].idproduct = models[m].id; + tmdc->dev[j].idversion = 0x0100; + + tmdc->dev[j].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + + for (i = 0; i < models[m].abs && i < TMDC_ABS; i++) { + set_bit(tmdc_abs[i], tmdc->dev[j].absbit); + tmdc->dev[j].absmin[tmdc_abs[i]] = 8; + tmdc->dev[j].absmax[tmdc_abs[i]] = 248; + tmdc->dev[j].absfuzz[tmdc_abs[i]] = 2; + tmdc->dev[j].absflat[tmdc_abs[i]] = 4; + } + + for (i = 0; i < models[m].hats && i < TMDC_ABS_HAT; i++) { + set_bit(tmdc_abs_hat[i], tmdc->dev[j].absbit); + tmdc->dev[j].absmin[tmdc_abs_hat[i]] = -1; + tmdc->dev[j].absmax[tmdc_abs_hat[i]] = 1; + } + + for (i = 0; i < models[m].joybtn && i < TMDC_BTN_JOY; i++) + set_bit(tmdc_btn_joy[i], tmdc->dev[j].keybit); + + for (i = 0; i < models[m].padbtn && i < TMDC_BTN_PAD; i++) + set_bit(tmdc_btn_pad[i], tmdc->dev[j].keybit); + + input_register_device(tmdc->dev + j); + printk(KERN_INFO "input%d: %s on gameport%d.%d\n", + tmdc->dev[j].number, tmdc->name[j], gameport->number, j); + } + + return; +fail2: gameport_close(gameport); +fail1: kfree(tmdc); +} + +static void tmdc_disconnect(struct gameport *gameport) +{ + struct tmdc *tmdc = gameport->private; + int i; + for (i = 0; i < 2; i++) + if (tmdc->exists & (1 << i)) + input_unregister_device(tmdc->dev + i); + gameport_close(gameport); + kfree(tmdc); +} + +static struct gameport_dev tmdc_dev = { + connect: tmdc_connect, + disconnect: tmdc_disconnect, +}; + +int __init tmdc_init(void) +{ + gameport_register_device(&tmdc_dev); + return 0; +} + +void __exit tmdc_exit(void) +{ + gameport_unregister_device(&tmdc_dev); +} + +module_init(tmdc_init); +module_exit(tmdc_exit); diff --git a/drivers/char/joystick/turbografx.c b/drivers/char/joystick/turbografx.c new file mode 100644 index 000000000..0e2dedcd0 --- /dev/null +++ b/drivers/char/joystick/turbografx.c @@ -0,0 +1,258 @@ +/* + * $Id: turbografx.c,v 1.8 2000/05/29 20:39:38 vojtech Exp $ + * + * Copyright (c) 1998-2000 Vojtech Pavlik + * + * Based on the work of: + * Steffen Schwenke + * + * Sponsored by SuSE + */ + +/* + * TurboGraFX parallel port interface driver for Linux. + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/parport.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); +MODULE_PARM(tgfx, "2-8i"); +MODULE_PARM(tgfx_2, "2-8i"); +MODULE_PARM(tgfx_3, "2-8i"); + +#define TGFX_REFRESH_TIME HZ/100 /* 10 ms */ + +#define TGFX_TRIGGER 0x08 +#define TGFX_UP 0x10 +#define TGFX_DOWN 0x20 +#define TGFX_LEFT 0x40 +#define TGFX_RIGHT 0x80 + +#define TGFX_THUMB 0x02 +#define TGFX_THUMB2 0x04 +#define TGFX_TOP 0x01 +#define TGFX_TOP2 0x08 + +static int tgfx[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_2[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; +static int tgfx_3[] __initdata = { -1, 0, 0, 0, 0, 0, 0, 0 }; + +static int tgfx_buttons[] = { BTN_TRIGGER, BTN_THUMB, BTN_THUMB2, BTN_TOP, BTN_TOP2 }; +static char *tgfx_name = "TurboGraFX Multisystem joystick"; + +struct tgfx { + struct pardevice *pd; + struct timer_list timer; + struct input_dev dev[7]; + int sticks; + int used; +} *tgfx_base[3]; + +/* + * tgfx_timer() reads and analyzes TurboGraFX joystick data. + */ + +static void tgfx_timer(unsigned long private) +{ + struct tgfx *tgfx = (void *) private; + struct input_dev *dev; + int data1, data2, i; + + for (i = 0; i < 7; i++) + if (tgfx->sticks & (1 << i)) { + + dev = tgfx->dev + i; + + parport_write_data(tgfx->pd->port, ~(1 << i)); + data1 = parport_read_status(tgfx->pd->port) ^ 0x7f; + data2 = parport_read_control(tgfx->pd->port) ^ 0x04; /* CAVEAT parport */ + + input_report_abs(dev, ABS_X, !!(data1 & TGFX_RIGHT) - !!(data1 & TGFX_LEFT)); + input_report_abs(dev, ABS_Y, !!(data1 & TGFX_DOWN ) - !!(data1 & TGFX_UP )); + + input_report_key(dev, BTN_TRIGGER, (data1 & TGFX_TRIGGER)); + input_report_key(dev, BTN_THUMB, (data2 & TGFX_THUMB )); + input_report_key(dev, BTN_THUMB2, (data2 & TGFX_THUMB2 )); + input_report_key(dev, BTN_TOP, (data2 & TGFX_TOP )); + input_report_key(dev, BTN_TOP2, (data2 & TGFX_TOP2 )); + } + + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); +} + +static int tgfx_open(struct input_dev *dev) +{ + struct tgfx *tgfx = dev->private; + if (!tgfx->used++) { + parport_claim(tgfx->pd); + parport_write_control(tgfx->pd->port, 0x04); + mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); + } + return 0; +} + +static void tgfx_close(struct input_dev *dev) +{ + struct tgfx *tgfx = dev->private; + if (!--tgfx->used) { + del_timer(&tgfx->timer); + parport_write_control(tgfx->pd->port, 0x00); + parport_release(tgfx->pd); + } +} + +/* + * tgfx_probe() probes for tg gamepads. + */ + +static struct tgfx __init *tgfx_probe(int *config) +{ + struct tgfx *tgfx; + struct parport *pp; + int i, j; + + if (config[0] < 0) + return NULL; + + for (pp = parport_enumerate(); pp && (config[0] > 0); pp = pp->next) + config[0]--; + + if (!pp) { + printk(KERN_ERR "turbografx.c: no such parport\n"); + return NULL; + } + + if (!(tgfx = kmalloc(sizeof(struct tgfx), GFP_KERNEL))) + return NULL; + memset(tgfx, 0, sizeof(struct tgfx)); + + tgfx->pd = parport_register_device(pp, "turbografx", NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); + + if (!tgfx->pd) { + printk(KERN_ERR "turbografx.c: parport busy already - lp.o loaded?\n"); + kfree(tgfx); + return NULL; + } + + init_timer(&tgfx->timer); + tgfx->timer.data = (long) tgfx; + tgfx->timer.function = tgfx_timer; + + tgfx->sticks = 0; + + for (i = 0; i < 7; i++) + if (config[i+1] > 0 && config[i+1] < 6) { + + tgfx->sticks |= (1 << i); + + tgfx->dev[i].private = tgfx; + tgfx->dev[i].open = tgfx_open; + tgfx->dev[i].close = tgfx_close; + + tgfx->dev[i].name = tgfx_name; + tgfx->dev[i].idbus = BUS_PARPORT; + tgfx->dev[i].idvendor = 0x0003; + tgfx->dev[i].idproduct = config[i+1]; + tgfx->dev[i].idversion = 0x0100; + + tgfx->dev[i].evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + tgfx->dev[i].absbit[0] = BIT(ABS_X) | BIT(ABS_Y); + + for (j = 0; j < config[i+1]; j++) + set_bit(tgfx_buttons[j], tgfx->dev[i].keybit); + + tgfx->dev[i].absmin[ABS_X] = -1; tgfx->dev[i].absmax[ABS_X] = 1; + tgfx->dev[i].absmin[ABS_Y] = -1; tgfx->dev[i].absmax[ABS_Y] = 1; + + input_register_device(tgfx->dev + i); + printk(KERN_INFO "input%d: %d-button Multisystem joystick on %s\n", + tgfx->dev[i].number, config[i+1], tgfx->pd->port->name); + } + + if (!tgfx->sticks) { + parport_unregister_device(tgfx->pd); + kfree(tgfx); + return NULL; + } + + return tgfx; +} + +#ifndef MODULE +int __init tgfx_setup(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx[i] = ints[i + 1]; + return 1; +} +int __init tgfx_setup_2(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx_2[i] = ints[i + 1]; + return 1; +} +int __init tgfx_setup_3(char *str) +{ + int i, ints[9]; + get_options(str, ARRAY_SIZE(ints), ints); + for (i = 0; i <= ints[0] && i < 8; i++) tgfx_3[i] = ints[i + 1]; + return 1; +} +__setup("tgfx=", tgfx_setup); +__setup("tgfx_2=", tgfx_setup_2); +__setup("tgfx_3=", tgfx_setup_3); +#endif + +int __init tgfx_init(void) +{ + tgfx_base[0] = tgfx_probe(tgfx); + tgfx_base[1] = tgfx_probe(tgfx_2); + tgfx_base[2] = tgfx_probe(tgfx_3); + + if (tgfx_base[0] || tgfx_base[1] || tgfx_base[2]) + return 0; + + return -ENODEV; +} + +void __exit tgfx_exit(void) +{ + int i, j; + + for (i = 0; i < 3; i++) + if (tgfx_base[i]) { + for (j = 0; j < 7; j++) + if (tgfx_base[i]->sticks & (1 << j)) + input_unregister_device(tgfx_base[i]->dev + j); + parport_unregister_device(tgfx_base[i]->pd); + } +} + +module_init(tgfx_init); +module_exit(tgfx_exit); diff --git a/drivers/char/joystick/warrior.c b/drivers/char/joystick/warrior.c new file mode 100644 index 000000000..7000b8560 --- /dev/null +++ b/drivers/char/joystick/warrior.c @@ -0,0 +1,212 @@ +/* + * $Id: warrior.c,v 1.8 2000/05/31 13:17:12 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * Logitech WingMan Warrior joystick driver for Linux + */ + +/* + * This program is free warftware; 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/serio.h> +#include <linux/init.h> + +/* + * Constants. + */ + +#define WARRIOR_MAX_LENGTH 16 +static char warrior_lengths[] = { 0, 4, 12, 3, 4, 4, 0, 0 }; +static char *warrior_name = "Logitech WingMan Warrior"; + +/* + * Per-Warrior data. + */ + +struct warrior { + struct input_dev dev; + int idx, len; + unsigned char data[WARRIOR_MAX_LENGTH]; +}; + +/* + * warrior_process_packet() decodes packets the driver receives from the + * Warrior. It updates the data accordingly. + */ + +static void warrior_process_packet(struct warrior *warrior) +{ + struct input_dev *dev = &warrior->dev; + unsigned char *data = warrior->data; + + if (!warrior->idx) return; + + switch ((data[0] >> 4) & 7) { + case 1: /* Button data */ + input_report_key(dev, BTN_TRIGGER, data[3] & 1); + input_report_key(dev, BTN_THUMB, (data[3] >> 1) & 1); + input_report_key(dev, BTN_TOP, (data[3] >> 2) & 1); + input_report_key(dev, BTN_TOP2, (data[3] >> 3) & 1); + return; + case 3: /* XY-axis info->data */ + input_report_abs(dev, ABS_X, ((data[0] & 8) << 5) - (data[2] | ((data[0] & 4) << 5))); + input_report_abs(dev, ABS_Y, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + return; + case 5: /* Throttle, spinner, hat info->data */ + input_report_abs(dev, ABS_THROTTLE, (data[1] | ((data[0] & 1) << 7)) - ((data[0] & 2) << 7)); + input_report_abs(dev, ABS_HAT0X, (data[3] & 2 ? 1 : 0) - (data[3] & 1 ? 1 : 0)); + input_report_abs(dev, ABS_HAT0Y, (data[3] & 8 ? 1 : 0) - (data[3] & 4 ? 1 : 0)); + input_report_rel(dev, REL_DIAL, (data[2] | ((data[0] & 4) << 5)) - ((data[0] & 8) << 5)); + return; + } +} + +/* + * warrior_interrupt() is called by the low level driver when characters + * are ready for us. We then buffer them for further processing, or call the + * packet processing routine. + */ + +static void warrior_interrupt(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct warrior* warrior = serio->private; + + if (data & 0x80) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = warrior_lengths[(data >> 4) & 7]; + } + + if (warrior->idx < warrior->len) + warrior->data[warrior->idx++] = data; + + if (warrior->idx == warrior->len) { + if (warrior->idx) warrior_process_packet(warrior); + warrior->idx = 0; + warrior->len = 0; + } +} + +/* + * warrior_disconnect() is the opposite of warrior_connect() + */ + +static void warrior_disconnect(struct serio *serio) +{ + struct warrior* warrior = serio->private; + input_unregister_device(&warrior->dev); + serio_close(serio); + kfree(warrior); +} + +/* + * warrior_connect() is the routine that is called when someone adds a + * new serio device. It looks for the Warrior, and if found, registers + * it as an input device. + */ + +static void warrior_connect(struct serio *serio, struct serio_dev *dev) +{ + struct warrior *warrior; + int i; + + if (serio->type != (SERIO_RS232 | SERIO_WARRIOR)) + return; + + if (!(warrior = kmalloc(sizeof(struct warrior), GFP_KERNEL))) + return; + + memset(warrior, 0, sizeof(struct warrior)); + + warrior->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); + warrior->dev.keybit[LONG(BTN_TRIGGER)] = BIT(BTN_TRIGGER) | BIT(BTN_THUMB) | BIT(BTN_TOP) | BIT(BTN_TOP2); + warrior->dev.relbit[0] = BIT(REL_DIAL); + warrior->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); + + warrior->dev.name = warrior_name; + warrior->dev.idbus = BUS_RS232; + warrior->dev.idvendor = SERIO_WARRIOR; + warrior->dev.idproduct = 0x0001; + warrior->dev.idversion = 0x0100; + + for (i = 0; i < 2; i++) { + warrior->dev.absmax[ABS_X+i] = -64; + warrior->dev.absmin[ABS_X+i] = 64; + warrior->dev.absflat[ABS_X+i] = 8; + } + + warrior->dev.absmax[ABS_THROTTLE] = -112; + warrior->dev.absmin[ABS_THROTTLE] = 112; + + for (i = 0; i < 2; i++) { + warrior->dev.absmax[ABS_HAT0X+i] = -1; + warrior->dev.absmin[ABS_HAT0X+i] = 1; + } + + warrior->dev.private = warrior; + + serio->private = warrior; + + if (serio_open(serio, dev)) { + kfree(warrior); + return; + } + + input_register_device(&warrior->dev); + + printk(KERN_INFO "input%d: Logitech WingMan Warrior on serio%d\n", warrior->dev.number, serio->number); +} + +/* + * The serio device structure. + */ + +static struct serio_dev warrior_dev = { + interrupt: warrior_interrupt, + connect: warrior_connect, + disconnect: warrior_disconnect, +}; + +/* + * The functions for inserting/removing us as a module. + */ + +int __init warrior_init(void) +{ + serio_register_device(&warrior_dev); + return 0; +} + +void __exit warrior_exit(void) +{ + serio_unregister_device(&warrior_dev); +} + +module_init(warrior_init); +module_exit(warrior_exit); diff --git a/drivers/char/lp.c b/drivers/char/lp.c index c2df7ea18..bbbcad9a7 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -675,9 +675,9 @@ static int lp_register(int nr, struct parport *port) lp_reset(nr); sprintf (name, "%d", nr); - devfs_register (devfs_handle, name, 0, + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, LP_MAJOR, nr, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &lp_fops, NULL); printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name, diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 40e6c7ba6..3b5a2c497 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -18,7 +18,6 @@ #include <linux/mman.h> #include <linux/random.h> #include <linux/init.h> -#include <linux/joystick.h> #include <linux/raw.h> #include <linux/capability.h> @@ -608,9 +607,9 @@ void __init memory_devfs_register (void) int i; for (i=0; i<(sizeof(list)/sizeof(*list)); i++) - devfs_register (NULL, list[i].name, 0, DEVFS_FL_NONE, + devfs_register (NULL, list[i].name, DEVFS_FL_NONE, MEM_MAJOR, list[i].minor, - list[i].mode | S_IFCHR, 0, 0, + list[i].mode | S_IFCHR, list[i].fops, NULL); } @@ -654,13 +653,6 @@ int __init chr_dev_init(void) #ifdef CONFIG_SPARCAUDIO sparcaudio_init(); #endif -#ifdef CONFIG_JOYSTICK - /* - * Some joysticks only appear when the sound card they are - * connected to is configured. Keep the sound/joystick ordering. - */ - js_init(); -#endif #if CONFIG_QIC02_TAPE qic02_tape_init(); #endif diff --git a/drivers/char/misc.c b/drivers/char/misc.c index df1e97494..c26a58eea 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -193,9 +193,9 @@ int misc_register(struct miscdevice * misc) if (!devfs_handle) devfs_handle = devfs_mk_dir (NULL, "misc", 4, NULL); misc->devfs_handle = - devfs_register (devfs_handle, misc->name, 0, DEVFS_FL_NONE, + devfs_register (devfs_handle, misc->name, DEVFS_FL_NONE, MISC_MAJOR, misc->minor, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, misc->fops, NULL); /* diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 819ed0b1a..af52cf98f 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -665,7 +665,7 @@ static int __init ppdev_init (void) devfs_handle = devfs_mk_dir (NULL, "parports", 0, NULL); devfs_register_series (devfs_handle, "%u", PARPORT_MAX, DEVFS_FL_DEFAULT, PP_MAJOR, 0, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &pp_fops, NULL); printk (KERN_INFO PP_VERSION "\n"); diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index fdaeeaea0..ba1b9caa9 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -3194,7 +3194,7 @@ int __init stl_init(void) devfs_handle = devfs_mk_dir (NULL, "staliomem", 9, NULL); devfs_register_series (devfs_handle, "%u", 4, DEVFS_FL_DEFAULT, STL_SIOMEMMAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &stl_fsiomem, NULL); /* diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 133f19aac..09b502a72 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -2905,37 +2905,37 @@ int __init qic02_tape_init(void) #endif return -ENODEV; } - devfs_register (NULL, "ntpqic11", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic11", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 2, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic11", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic11", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 3, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic24", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic24", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 4, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic24", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic24", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 5, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic120", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic120", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 6, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic120", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic120", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 7, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "ntpqic150", 0, DEVFS_FL_NONE, + devfs_register (NULL, "ntpqic150", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 8, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); - devfs_register (NULL, "tpqic150", 0, DEVFS_FL_NONE, + devfs_register (NULL, "tpqic150", DEVFS_FL_DEFAULT, QIC02_TAPE_MAJOR, 9, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &qic02_tape_fops, NULL); init_waitqueue_head(&qic02_tape_transfer); /* prepare timer */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 8a03fed26..613b2f967 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1971,8 +1971,6 @@ void tty_register_devfs (struct tty_driver *driver, unsigned int flags, { #ifdef CONFIG_DEVFS_FS umode_t mode = S_IFCHR | S_IRUSR | S_IWUSR; - uid_t uid = 0; - gid_t gid = 0; struct tty_struct tty; char buf[32]; @@ -1996,14 +1994,11 @@ void tty_register_devfs (struct tty_driver *driver, unsigned int flags, } # ifdef CONFIG_UNIX98_PTYS if ( (driver->major >= UNIX98_PTY_SLAVE_MAJOR) && - (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) { - uid = current->uid; - gid = current->gid; - } + (driver->major < UNIX98_PTY_SLAVE_MAJOR + UNIX98_NR_MAJORS) ) + flags |= DEVFS_FL_CURRENT_OWNER; # endif - devfs_register (NULL, tty_name (&tty, buf), 0,flags | DEVFS_FL_DEFAULT, - driver->major, minor, mode, uid, gid, - &tty_fops, NULL); + devfs_register (NULL, tty_name (&tty, buf), flags | DEVFS_FL_DEFAULT, + driver->major, minor, mode, &tty_fops, NULL); #endif /* CONFIG_DEVFS_FS */ } diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c index 5138c76b8..67ff8d856 100644 --- a/drivers/char/vc_screen.c +++ b/drivers/char/vc_screen.c @@ -476,12 +476,12 @@ void vcs_make_devfs (unsigned int index, int unregister) } else { - devfs_register (devfs_handle, name + 1, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, name + 1, DEVFS_FL_DEFAULT, VCS_MAJOR, index + 1, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); - devfs_register (devfs_handle, name, 0, DEVFS_FL_DEFAULT, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, name, DEVFS_FL_DEFAULT, VCS_MAJOR, index + 129, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); } #endif /* CONFIG_DEVFS_FS */ } @@ -496,12 +496,12 @@ int __init vcs_init(void) printk("unable to get major %d for vcs device", VCS_MAJOR); devfs_handle = devfs_mk_dir (NULL, "vcc", 3, NULL); - devfs_register (devfs_handle, "0", 1, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, "0", DEVFS_FL_DEFAULT, VCS_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); - devfs_register (devfs_handle, "a", 1, DEVFS_FL_DEFAULT, + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); + devfs_register (devfs_handle, "a", DEVFS_FL_DEFAULT, VCS_MAJOR, 128, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, &vcs_fops, NULL); + S_IFCHR | S_IRUSR | S_IWUSR, &vcs_fops, NULL); return error; } diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index ababd833f..4a3bfb859 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -62,7 +62,7 @@ LIST_HEAD(videodev_proc_list); #ifdef CONFIG_VIDEO_BT848 -extern int tuner_init_module(struct video_init *); +extern int i2c_tuner_init(struct video_init *); #endif #ifdef CONFIG_VIDEO_BWQCAM extern int init_bw_qcams(struct video_init *); @@ -79,7 +79,7 @@ extern int init_zoran_cards(struct video_init *); static struct video_init video_init_list[]={ #ifdef CONFIG_VIDEO_BT848 - {"i2c-tuner", tuner_init_module}, + {"i2c-tuner", i2c_tuner_init}, #endif #ifdef CONFIG_VIDEO_BWQCAM {"bw-qcam", init_bw_qcams}, @@ -286,6 +286,14 @@ static int videodev_proc_read(char *page, char **start, off_t off, PRINT_VID_TYPE(VID_TYPE_MJPEG_ENCODER); out += sprintf (out, "\n"); out += sprintf (out, "hardware : 0x%x\n", vfd->hardware); +#if 0 + out += sprintf (out, "channels : %d\n", d->vcap.channels); + out += sprintf (out, "audios : %d\n", d->vcap.audios); + out += sprintf (out, "maxwidth : %d\n", d->vcap.maxwidth); + out += sprintf (out, "maxheight : %d\n", d->vcap.maxheight); + out += sprintf (out, "minwidth : %d\n", d->vcap.minwidth); + out += sprintf (out, "minheight : %d\n", d->vcap.minheight); +#endif skip: len = out - page; @@ -351,6 +359,8 @@ static void videodev_proc_create_dev (struct video_device *vfd, char *name) d->vdev = vfd; strcpy (d->name, name); + /* How can I get capability information ? */ + list_add (&d->proc_list, &videodev_proc_list); } @@ -459,9 +469,9 @@ int video_register_device(struct video_device *vfd, int type) * has serious privacy issues. */ vfd->devfs_handle = - devfs_register (NULL, name, 0, DEVFS_FL_DEFAULT, + devfs_register (NULL, name, DEVFS_FL_DEFAULT, VIDEO_MAJOR, vfd->minor, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &video_fops, NULL); #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) @@ -564,4 +574,3 @@ EXPORT_SYMBOL(video_unregister_device); MODULE_AUTHOR("Alan Cox"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers"); - diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index ab4d3afbb..53f6b5a9e 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -2199,9 +2199,9 @@ static int ide_cdrom_register (ide_drive_t *drive, int nslots) if (!CDROM_CONFIG_FLAGS (drive)->close_tray) devinfo->mask |= CDC_CLOSE_TRAY; - devinfo->de = devfs_register(drive->de, "cd", 2, DEVFS_FL_DEFAULT, + devinfo->de = devfs_register(drive->de, "cd", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, ide_fops, NULL); return register_cdrom(devinfo); diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 09e1b12fb..911234014 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -5987,14 +5987,14 @@ int idetape_init (void) idetape_setup (drive, tape, minor); idetape_chrdevs[minor].drive = drive; tape->de_r = - devfs_register (drive->de, "mt", 2, DEVFS_FL_DEFAULT, + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &idetape_fops, NULL); tape->de_n = - devfs_register (drive->de, "mtn", 3, DEVFS_FL_DEFAULT, + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, HWIF(drive)->major, minor + 128, - S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + S_IFCHR | S_IRUGO | S_IWUGO, &idetape_fops, NULL); devfs_register_tape (tape->de_r); supported++; failed--; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index 932c6f114..b9aa05d4d 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -805,7 +805,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err) spin_lock_irqsave(&io_request_lock, flags); blkdev_dequeue_request(rq); HWGROUP(drive)->rq = NULL; - rq->rq_status = RQ_INACTIVE; + blkdev_release_request(rq); spin_unlock_irqrestore(&io_request_lock, flags); if (rq->sem != NULL) up(rq->sem); /* inform originator that rq has been serviced */ diff --git a/drivers/isdn/avmb1/capi.c b/drivers/isdn/avmb1/capi.c index 972019295..8169df482 100644 --- a/drivers/isdn/avmb1/capi.c +++ b/drivers/isdn/avmb1/capi.c @@ -2053,11 +2053,11 @@ int capi_init(void) devfs_register_series (NULL, "capi/r%u", CAPINC_NR_PORTS, DEVFS_FL_DEFAULT, capi_rawmajor, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &capinc_raw_fops, NULL); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - devfs_register (NULL, "isdn/capi20", 0, DEVFS_FL_DEFAULT, - capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + devfs_register (NULL, "isdn/capi20", DEVFS_FL_DEFAULT, + capi_major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &capi_fops, NULL); printk(KERN_NOTICE "capi20: started up with major %d\n", capi_major); diff --git a/drivers/isdn/isdn_common.c b/drivers/isdn/isdn_common.c index aca716184..fa5a3d844 100644 --- a/drivers/isdn/isdn_common.c +++ b/drivers/isdn/isdn_common.c @@ -2581,14 +2581,14 @@ static void isdn_register_devfs(int k) sprintf (buf, "isdn%d", k); dev->devfs_handle_isdnX[k] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, - ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, 0, 0, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_B + k,0600 | S_IFCHR, &isdn_fops, NULL); sprintf (buf, "isdnctrl%d", k); dev->devfs_handle_isdnctrlX[k] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_CTRL + k, 0600 | S_IFCHR, - 0, 0, &isdn_fops, NULL); + &isdn_fops, NULL); } static void isdn_unregister_devfs(int k) @@ -2610,19 +2610,19 @@ static void isdn_init_devfs(void) sprintf (buf, "ippp%d", i); dev->devfs_handle_ipppX[i] = - devfs_register (devfs_handle, buf, 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, buf, DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_PPP + i, - 0600 | S_IFCHR, 0, 0, &isdn_fops, NULL); + 0600 | S_IFCHR, &isdn_fops, NULL); } # endif dev->devfs_handle_isdninfo = - devfs_register (devfs_handle, "isdninfo", 0, DEVFS_FL_DEFAULT, + devfs_register (devfs_handle, "isdninfo", DEVFS_FL_DEFAULT, ISDN_MAJOR, ISDN_MINOR_STATUS, 0600 | S_IFCHR, - 0, 0, &isdn_fops, NULL); + &isdn_fops, NULL); dev->devfs_handle_isdnctrl = - devfs_register (devfs_handle, "isdnctrl", 0, DEVFS_FL_DEFAULT, - ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, 0, 0, + devfs_register (devfs_handle, "isdnctrl", DEVFS_FL_DEFAULT, + ISDN_MAJOR, ISDN_MINOR_CTRL, 0600 | S_IFCHR, &isdn_fops, NULL); } diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 2fc74f806..ff6f18ee1 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -674,8 +674,8 @@ void adbdev_init() if (devfs_register_chrdev(ADB_MAJOR, "adb", &adb_fops)) printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR); else - devfs_register (NULL, "adb", 0, DEVFS_FL_NONE, + devfs_register (NULL, "adb", DEVFS_FL_DEFAULT, ADB_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &adb_fops, NULL); } diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 7592e0bc8..5084ea70e 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -197,7 +197,7 @@ endmenu bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then - dep_tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX $CONFIG_FDDI + tristate ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX tristate ' SysKonnect FDDI PCI support' CONFIG_SKFP fi diff --git a/drivers/net/dmfe.c b/drivers/net/dmfe.c index d0f4257da..b425513b0 100644 --- a/drivers/net/dmfe.c +++ b/drivers/net/dmfe.c @@ -1,8 +1,9 @@ /* - dmfe.c: Version 1.28 01/18/2000 + dmfe.c: Version 1.30 06/11/2000 A Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver for Linux. Copyright (C) 1997 Sten Wang + (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -46,20 +47,29 @@ Made it compile in 2.3 (device to net_device) Alan Cox <alan@redhat.com> : + Cleaned up for kernel merge. Removed the back compatibility support Reformatted, fixing spelling etc as I went Removed IRQ 0-15 assumption - + + Jeff Garzik <jgarzik@mandrakesoft.com> : + Updated to use new PCI driver API. + Resource usage cleanups. + Report driver version to user. + TODO - + + Implement pci_driver::suspend() and pci_driver::resume() + power management methods. + Check and fix on 64bit and big endian boxes. - Sort out the PCI latency. - - (C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved. - - Cleaned up for kernel merge by Alan Cox (alan@redhat.com) + + Test and make sure PCI latency is now correct for all cases. + */ +#define DMFE_VERSION "1.30 (June 11, 2000)" + #include <linux/module.h> #include <linux/kernel.h> @@ -142,7 +152,9 @@ #define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);DELAY_5US;outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);DELAY_5US; -#define CHK_IO_SIZE(pci_id, dev_rev) ( (pci_id==PCI_DM9132_ID) || (dev_rev >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE +#define CHK_IO_SIZE(pci_dev, dev_rev) \ + __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) /* Structure/enum declaration ------------------------------- */ @@ -164,7 +176,7 @@ struct rx_desc { struct dmfe_board_info { u32 chip_id; /* Chip vendor/Device ID */ - u32 chip_revesion; /* Chip revesion */ + u32 chip_revision; /* Chip revision */ struct net_device *next_dev; /* next device */ struct pci_dev *net_dev; /* PCI device */ @@ -221,7 +233,6 @@ enum dmfe_CR6_bits { /* Global variable declaration ----------------------------- */ static int dmfe_debug = 0; static unsigned char dmfe_media_mode = 8; -static struct net_device *dmfe_root_dev = NULL; /* First device */ static u32 dmfe_cr6_user_set = 0; /* For module input parameter */ @@ -299,7 +310,6 @@ static unsigned long CrcTable[256] = }; /* function declaration ------------------------------------- */ -static int dmfe_probe(void); static int dmfe_open(struct net_device *); static int dmfe_start_xmit(struct sk_buff *, struct net_device *); static int dmfe_stop(struct net_device *); @@ -333,111 +343,117 @@ static unsigned long cal_CRC(unsigned char *, unsigned int, u8); * Search DM910X board, allocate space and register it */ -static int __init dmfe_probe(void) + +static int __init dmfe_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { unsigned long pci_iobase; - u16 dm9102_count = 0; u8 pci_irqline; - static int index = 0; /* For multiple call */ struct dmfe_board_info *db; /* Point a board information structure */ int i; - struct pci_dev *net_dev = NULL; struct net_device *dev; + u32 dev_rev; DMFE_DBUG(0, "dmfe_probe()", 0); - if (!pci_present()) - return -ENODEV; + pci_iobase = pci_resource_start(pdev, 0); + pci_irqline = pdev->irq; - index = 0; - while ((net_dev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, net_dev))) - { - u32 pci_id; - u32 dev_rev; + /* Interrupt check */ + if (pci_irqline == 0) { + printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", + pci_irqline); + goto err_out; + } - index++; - if (pci_read_config_dword(net_dev, PCI_VENDOR_ID, &pci_id) != DMFE_SUCC) - continue; + /* iobase check */ + if (pci_iobase == 0) { + printk(KERN_ERR "dmfe: I/O base is zero\n"); + goto err_out; + } - if ((net_dev->device != PCI_DM9102_ID) && (net_dev->device != PCI_DM9132_ID)) - continue; + /* Enable Master/IO access, Disable memory access */ + if (pci_enable_device(pdev)) + goto err_out; + pci_set_master(pdev); - pci_iobase = pci_resource_start (net_dev, 0); - pci_irqline = net_dev->irq; - - /* Enable Master/IO access, Disable memory access */ - - if (pci_enable_device(net_dev)) - continue; - pci_set_master(net_dev); - - /* Set Latency Timer 80h */ - - /* FIXME: setting values > 32 breaks some SiS 559x stuff. - Need a PCI quirk.. */ - - pci_write_config_byte(net_dev, PCI_LATENCY_TIMER, 0x80); +#if 0 /* pci_{enable_device,set_master} sets minimum latency for us now */ - /* Read Chip revesion */ - pci_read_config_dword(net_dev, PCI_REVISION_ID, &dev_rev); - - /* IO range check */ - if (check_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev))) { - continue; - } - /* Interrupt check */ - if (pci_irqline == 0) { - printk(KERN_ERR "dmfe: Interrupt wrong : IRQ=%d\n", pci_irqline); - continue; - } + /* Set Latency Timer 80h */ + /* FIXME: setting values > 32 breaks some SiS 559x stuff. + Need a PCI quirk.. */ - /* Found DM9102 card and PCI resource allocated OK */ - dm9102_count++; /* Found a DM9102 card */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x80); +#endif - /* Init network device */ - dev = init_etherdev(NULL, sizeof(*db)); - if (dev == NULL) - continue; - - db = dev->priv; + /* Read Chip revision */ + pci_read_config_dword(pdev, PCI_REVISION_ID, &dev_rev); + + /* Init network device */ + dev = init_etherdev(NULL, sizeof(*db)); + if (dev == NULL) + goto err_out; - memset(db, 0, sizeof(*db)); - db->next_dev = dmfe_root_dev; - dmfe_root_dev = dev; + /* IO range check */ + if (!request_region(pci_iobase, CHK_IO_SIZE(pdev, dev_rev), dev->name)) { + printk(KERN_ERR "dmfe: I/O conflict : IO=%lx Range=%x\n", + pci_iobase, CHK_IO_SIZE(pdev, dev_rev)); + goto err_out_netdev; + } - db->chip_id = pci_id; /* keep Chip vandor/Device ID */ - db->ioaddr = pci_iobase; - db->chip_revesion = dev_rev; + db = dev->priv; + pdev->driver_data = dev; - db->net_dev = net_dev; + db->chip_id = ent->driver_data; + db->ioaddr = pci_iobase; + db->chip_revision = dev_rev; - dev->base_addr = pci_iobase; - dev->irq = pci_irqline; - dev->open = &dmfe_open; - dev->hard_start_xmit = &dmfe_start_xmit; - dev->stop = &dmfe_stop; - dev->get_stats = &dmfe_get_stats; - dev->set_multicast_list = &dmfe_set_filter_mode; - dev->do_ioctl = &dmfe_do_ioctl; + db->net_dev = pdev; - request_region(pci_iobase, CHK_IO_SIZE(pci_id, dev_rev), dev->name); + dev->base_addr = pci_iobase; + dev->irq = pci_irqline; + dev->open = &dmfe_open; + dev->hard_start_xmit = &dmfe_start_xmit; + dev->stop = &dmfe_stop; + dev->get_stats = &dmfe_get_stats; + dev->set_multicast_list = &dmfe_set_filter_mode; + dev->do_ioctl = &dmfe_do_ioctl; - /* read 64 word srom data */ - for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i); + /* read 64 word srom data */ + for (i = 0; i < 64; i++) + ((u16 *) db->srom)[i] = read_srom_word(pci_iobase, i); - /* Set Node address */ - for (i = 0; i < 6; i++) - dev->dev_addr[i] = db->srom[20 + i]; + /* Set Node address */ + for (i = 0; i < 6; i++) + dev->dev_addr[i] = db->srom[20 + i]; + + return 0; + +err_out_netdev: + unregister_netdev(dev); + kfree(dev); +err_out: + return -ENODEV; +} - } - if (!dm9102_count) - printk(KERN_WARNING "dmfe: Can't find DM910X board\n"); +static void __exit dmfe_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pdev->driver_data; + struct dmfe_board_info *db; + + DMFE_DBUG(0, "dmfe_remove_one()", 0); + + db = dev->priv; + + unregister_netdev(dev); + release_region(dev->base_addr, CHK_IO_SIZE(pdev, db->chip_revision)); + kfree(dev); /* free board information */ - return dm9102_count ? 0 : -ENODEV; + DMFE_DBUG(0, "dmfe_remove_one() exit", 0); } + /* * Open the interface. * The interface is opened whenever "ifconfig" actives it. @@ -482,7 +498,7 @@ static int dmfe_open(struct net_device *dev) db->in_reset_state = 0; db->rx_error_cnt = 0; - if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revesion >= 0x02000030)) { + if (!chkmode || (db->chip_id == PCI_DM9132_ID) || (db->chip_revision >= 0x02000030)) { //db->cr6_data &= ~CR6_SFT; /* Used Tx threshold */ //db->cr6_data |= CR6_NO_PURGE; /* No purge if rx unavailable */ db->cr0_data = 0xc00000; /* TX/RX desc burst mode */ @@ -933,8 +949,8 @@ static void dmfe_timer(unsigned long data) else tmp_cr12 = inb(db->ioaddr + DCR12); /* DM9102/DM9102A */ - if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revesion == 0x02000030)) || - ((db->chip_id == PCI_DM9132_ID) && (db->chip_revesion == 0x02000010))) { + if (((db->chip_id == PCI_DM9102_ID) && (db->chip_revision == 0x02000030)) || + ((db->chip_id == PCI_DM9132_ID) && (db->chip_revision == 0x02000010))) { /* DM9102A Chip */ if (tmp_cr12 & 2) tmp_cr12 = 0x0; /* Link failed */ @@ -1532,6 +1548,21 @@ unsigned long cal_CRC(unsigned char *Data, unsigned int Len, u8 flag) } +static struct pci_device_id dmfe_pci_tbl[] __initdata = { + { 0x1282, 0x9132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9132_ID }, + { 0x1282, 0x9102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9102_ID }, + { 0x1282, 0x9100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_DM9100_ID }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); + +static struct pci_driver dmfe_driver = { + name: "dmfe", + id_table: dmfe_pci_tbl, + probe: dmfe_init_one, + remove: dmfe_remove_one, +}; + MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); MODULE_DESCRIPTION("Davicom DM910X fast ethernet driver"); MODULE_PARM(debug, "i"); @@ -1546,6 +1577,8 @@ MODULE_PARM(chkmode, "i"); static int __init dmfe_init_module(void) { + int rc; + DMFE_DBUG(0, "init_module() ", debug); if (debug) @@ -1565,32 +1598,26 @@ static int __init dmfe_init_module(void) break; } - return dmfe_probe(); /* search board and register */ + rc = pci_register_driver(&dmfe_driver); + if (rc < 0) + return rc; + if (rc > 0) { + printk (KERN_INFO "Davicom DM91xx net driver loaded, version " + DMFE_VERSION "\n"); + return 0; + } + return -ENODEV; } /* * Description: * when user used rmmod to delete module, system invoked clean_module() - * to un-register device. + * to un-register all registered services. */ static void __exit dmfe_cleanup_module(void) { - struct net_device *next_dev; - struct dmfe_board_info *db; - - DMFE_DBUG(0, "clean_module()", 0); - - while (dmfe_root_dev) { - db = dmfe_root_dev->priv; - next_dev = db->next_dev; - unregister_netdev(dmfe_root_dev); - release_region(dmfe_root_dev->base_addr, CHK_IO_SIZE(db->chip_id, db->chip_revesion)); - kfree(db); /* free board information */ - kfree(dmfe_root_dev); /* free device structure */ - dmfe_root_dev = next_dev; - } - DMFE_DBUG(0, "clean_module() exit", 0); + pci_unregister_driver(&dmfe_driver); } module_init(dmfe_init_module); diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c index bec5eb631..ccb0d42de 100644 --- a/drivers/net/dummy.c +++ b/drivers/net/dummy.c @@ -110,12 +110,12 @@ int __init dummy_init(struct net_device *dev) static int dummy_xmit(struct sk_buff *skb, struct net_device *dev) { struct net_device_stats *stats; - dev_kfree_skb(skb); stats = (struct net_device_stats *)dev->priv; stats->tx_packets++; stats->tx_bytes+=skb->len; + dev_kfree_skb(skb); return 0; } diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index bb6314456..03944f066 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -941,12 +941,7 @@ static int irport_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; irda_task_execute(self, __irport_change_speed, NULL, NULL, (void *) irq->ifr_baudrate); diff --git a/drivers/net/irda/irtty.c b/drivers/net/irda/irtty.c index c174dfa71..1fd3acda9 100644 --- a/drivers/net/irda/irtty.c +++ b/drivers/net/irda/irtty.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli <dagb@cs.uit.no> * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Tue Apr 25 21:17:49 2000 + * Modified at: Sat Mar 11 07:43:30 2000 * Modified by: Dag Brattli <dagb@cs.uit.no> * Sources: slip.c by Laurence Culhane, <loz@holmes.demon.co.uk> * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> @@ -962,12 +962,7 @@ static int irtty_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; irda_task_execute(self, irtty_change_speed, NULL, NULL, (void *) irq->ifr_baudrate); diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 4fe1a764b..df9c63621 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -56,7 +56,6 @@ #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> -#include <asm/hardirq.h> #include <linux/pm.h> @@ -1948,15 +1947,8 @@ static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) { - IRDA_DEBUG(0, __FUNCTION__ "(), not capable sysadm\n"); + if (!capable(CAP_NET_ADMIN)) return -EPERM; - } nsc_ircc_change_speed(self, irq->ifr_baudrate); break; case SIOCSMEDIABUSY: /* Set media busy */ diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c index 1f1f2cb14..387208e2f 100644 --- a/drivers/net/irda/toshoboe.c +++ b/drivers/net/irda/toshoboe.c @@ -603,12 +603,7 @@ static int toshoboe_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; /* toshoboe_setbaud(self, irq->ifr_baudrate); */ /* Just change speed once - inserted by Paul Bristow */ @@ -722,7 +717,7 @@ toshoboe_open (struct pci_dev *pci_dev) self->open = 0; self->stopped = 0; self->pdev = pci_dev; - self->base = pci_resource_start (pci_dev, 0); + self->base = pci_dev->resource[0].start; self->io.sir_base = self->base; self->io.irq = pci_dev->irq; @@ -905,6 +900,7 @@ toshoboe_gotosleep (struct toshoboe_cb *self) static void toshoboe_wakeup (struct toshoboe_cb *self) { + struct net_device *dev = self->netdev; unsigned long flags; if (!self->stopped) @@ -956,26 +952,36 @@ int __init toshoboe_init (void) struct pci_dev *pci_dev = NULL; int found = 0; - while ((pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, - PCI_DEVICE_ID_FIR701, pci_dev)) != NULL) { - if (pci_enable_device(pci_dev)) - continue; + do + { + pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA, + PCI_DEVICE_ID_FIR701, pci_dev); + if (pci_dev) + { printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n", - pci_resource_start (pci_dev, 0), + pci_dev->resource[0].start, pci_dev->irq); if (!toshoboe_open (pci_dev)) found++; - } + } + + } + while (pci_dev); + if (found) + { return 0; + } return -ENODEV; } +#ifdef MODULE -static void __exit toshoboe_cleanup (void) +static void +toshoboe_cleanup (void) { int i; @@ -991,8 +997,19 @@ static void __exit toshoboe_cleanup (void) } -#ifdef MODULE -module_init(toshoboe_init); -#endif -module_exit(toshoboe_cleanup); +int +init_module (void) +{ + return toshoboe_init (); +} + + +void +cleanup_module (void) +{ + toshoboe_cleanup (); +} + + +#endif diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index c262c7e30..2456e012d 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -255,12 +255,13 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, dev->get_stats = w83977af_net_get_stats; rtnl_lock(); - err = register_netdevice(dev); + err = register_netdev(dev); rtnl_unlock(); if (err) { - ERROR(__FUNCTION__ "(), register_netdevice() failed!\n"); + ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); return -1; } + MESSAGE("IrDA: Registered device %s\n", dev->name); return 0; @@ -301,7 +302,7 @@ static int w83977af_close(struct w83977af_ir *self) /* Release the PORT that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.fir_base); + self->io.fir_base); release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) @@ -1331,12 +1332,7 @@ static int w83977af_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - /* - * This function will also be used by IrLAP to change the - * speed, so we still must allow for speed change within - * interrupt context. - */ - if (!in_interrupt() && !capable(CAP_NET_ADMIN)) + if (!capable(CAP_NET_ADMIN)) return -EPERM; w83977af_change_speed(self, irq->ifr_baudrate); break; diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index b9d5ebfc3..e43e3b394 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -718,9 +718,9 @@ int __init ppp_init(void) err = devfs_register_chrdev(PPP_MAJOR, "ppp", &ppp_device_fops); if (err) printk(KERN_ERR "failed to register PPP device (%d)\n", err); - devfs_handle = devfs_register(NULL, "ppp", 0, DEVFS_FL_NONE, + devfs_handle = devfs_register(NULL, "ppp", DEVFS_FL_DEFAULT, PPP_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &ppp_device_fops, NULL); return 0; diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 6885d5fb1..c27d89ca9 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -398,7 +398,7 @@ static int __init cosa_init(void) devfs_handle = devfs_mk_dir (NULL, "cosa", 4, NULL); devfs_register_series (devfs_handle, "%u", nr_cards, DEVFS_FL_DEFAULT, cosa_major, 0, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &cosa_fops, NULL); if (!nr_cards) { printk(KERN_WARNING "cosa: no devices found.\n"); diff --git a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c index a945167c5..36d0a5d6a 100644 --- a/drivers/pcmcia/yenta.c +++ b/drivers/pcmcia/yenta.c @@ -127,6 +127,8 @@ static int yenta_get_status(pci_socket_t *socket, unsigned int *value) val = (state & CB_3VCARD) ? SS_3VCARD : 0; val |= (state & CB_XVCARD) ? SS_XVCARD : 0; + val |= (state & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD + | CB_XVCARD | CB_YVCARD)) ? 0 : SS_PENDING; if (state & CB_CBCARD) { val |= SS_CARDBUS; @@ -556,30 +558,6 @@ static void yenta_clear_maps(pci_socket_t *socket) } } -/* - * Many chipsets (all TI chips?) seem to have - * problems sensing the power state of the card - * that was inserted at chip init time, so force - * it if necessary.. - */ -static void yenta_power_sense(pci_socket_t *socket) -{ - u32 status = cb_readl(socket, CB_SOCKET_STATE); - - /* - * Nothing inserted, nothing to sense.. - * ..or sense status already available. - */ - if (status & (CB_CDETECT1 | CB_CDETECT2 | CB_5VCARD | CB_3VCARD | CB_XVCARD | CB_YVCARD)) - return; - - /* - * Ho humm. It reports a card, but it doesn't report - * any voltages. Need to redo the VS test.. - */ - cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); -} - /* Called at resume and initialization events */ static int yenta_init(pci_socket_t *socket) { @@ -620,7 +598,8 @@ static int yenta_init(pci_socket_t *socket) exca_writeb(socket, I365_GBLCTL, 0x00); exca_writeb(socket, I365_GENCTL, 0x00); - yenta_power_sense(socket); + /* Redo card voltage interrogation */ + cb_writel(socket, CB_SOCKET_FORCE, CB_CVSTEST); yenta_clear_maps(socket); return 0; diff --git a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c index 497879619..2115cdac9 100644 --- a/drivers/pnp/isapnp.c +++ b/drivers/pnp/isapnp.c @@ -500,6 +500,7 @@ static void __init isapnp_add_irq_resource(struct pci_dev *dev, int dependent, int size) { unsigned char tmp[3]; + int i; struct isapnp_irq *irq, *ptr; isapnp_peek(tmp, size); @@ -526,6 +527,9 @@ static void __init isapnp_add_irq_resource(struct pci_dev *dev, ptr->next = irq; else (*res)->irq = irq; + for (i=0; i<16; i++) + if (irq->map & i) + pcibios_penalize_isa_irq(i); } /* @@ -1603,6 +1607,14 @@ static int isapnp_check_interrupt(struct isapnp_cfgtmp *cfg, int irq, int idx) return 1; } } +#ifdef CONFIG_PCI + if (!isapnp_skip_pci_scan) { + pci_for_each_dev(dev) { + if (dev->irq == irq) + return 1; + } + } +#endif if (request_irq(irq, isapnp_test_handler, SA_INTERRUPT, "isapnp", NULL)) return 1; free_irq(irq, NULL); @@ -2070,45 +2082,6 @@ static void isapnp_free_all_resources(void) #endif } -static int __init isapnp_do_reserve_irq(int irq) -{ - int i; - - if (irq < 0 || irq > 15) - return -EINVAL; - for (i = 0; i < 16; i++) { - if (isapnp_reserve_irq[i] == irq) - return 0; - } - for (i = 0; i < 16; i++) { - if (isapnp_reserve_irq[i] < 0) { - isapnp_reserve_irq[i] = irq; -#ifdef ISAPNP_DEBUG - printk("isapnp: IRQ %i is reserved now.\n", irq); -#endif - return 0; - } - } - return -ENOMEM; -} - -#ifdef CONFIG_PCI - -static void __init isapnp_pci_init(void) -{ - struct pci_dev *dev; - - pci_for_each_dev(dev) { -#ifdef ISAPNP_DEBUG - printk("isapnp: PCI: reserved IRQ: %i\n", dev->irq); -#endif - if (dev->irq > 0) - isapnp_do_reserve_irq(dev->irq); - } -} - -#endif /* CONFIG_PCI */ - EXPORT_SYMBOL(isapnp_cards); EXPORT_SYMBOL(isapnp_devices); EXPORT_SYMBOL(isapnp_present); @@ -2200,10 +2173,6 @@ int __init isapnp_init(void) } else { printk("isapnp: No Plug & Play card found\n"); } -#ifdef CONFIG_PCI - if (!isapnp_skip_pci_scan) - isapnp_pci_init(); -#endif #ifdef CONFIG_PROC_FS isapnp_proc_init(); #endif diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 79e8f27dc..55e27c7d8 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -792,11 +792,10 @@ register_dasd_last (int di) dasd_info[di]->info.devno,'\0' ); dasd_info[di] -> devfs_entry = devfs_register ( NULL /* dir */, - name, strlen(name), - 0 /* flags */, + name, + DEVFS_FL_DEFAULT /* flags */, DASD_MAJOR, minor, 0755 /* mode */, - 0 /* uid */ , 0 /* gid */, &dasd_device_operations, (void *)dasd_info[di]); } diff --git a/drivers/sbus/audio/audio.c b/drivers/sbus/audio/audio.c index 31856af31..586945b10 100644 --- a/drivers/sbus/audio/audio.c +++ b/drivers/sbus/audio/audio.c @@ -1,4 +1,4 @@ -/* $Id: audio.c,v 1.51 2000/06/19 06:24:47 davem Exp $ +/* $Id: audio.c,v 1.52 2000/06/22 11:42:27 davem Exp $ * drivers/sbus/audio/audio.c * * Copyright 1996 Thomas K. Dyas (tdyas@noc.rutgers.edu) @@ -2010,9 +2010,9 @@ int register_sparcaudio_driver(struct sparcaudio_driver *drv, int duplex) for (i=0; i < sizeof (dev_list) / sizeof (*dev_list); i++) { sparcaudio_mkname (name_buf, dev_list[i].name, dev); minor = (dev << SPARCAUDIO_DEVICE_SHIFT) | dev_list[i].minor; - devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE, + devfs_register (devfs_handle, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, minor, S_IFCHR | dev_list[i].mode, - 0, 0, &sparcaudio_fops, NULL); + &sparcaudio_fops, NULL); } /* Setup the circular queues of output and input buffers diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c index ba6810017..6dfc4c132 100644 --- a/drivers/sbus/char/bpp.c +++ b/drivers/sbus/char/bpp.c @@ -1031,7 +1031,7 @@ int __init bpp_init(void) } devfs_handle = devfs_mk_dir (NULL, "bpp", 3, NULL); devfs_register_series (devfs_handle, "%u", BPP_NO, DEVFS_FL_DEFAULT, - BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + BPP_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &bpp_fops, NULL); return 0; diff --git a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c index d31ef5193..458fe20af 100644 --- a/drivers/sbus/char/jsflash.c +++ b/drivers/sbus/char/jsflash.c @@ -271,7 +271,7 @@ static loff_t jsf_lseek(struct file * file, loff_t offset, int orig) } /* - * P3: OS SIMM Cannot be read in other size but a 32bits word. + * OS SIMM Cannot be read in other size but a 32bits word. */ static ssize_t jsf_read(struct file * file, char * buf, size_t togo, loff_t *ppos) @@ -647,8 +647,7 @@ int jsfd_init(void) { int i; if (jsf0.base == 0) { - printk("jsfd_init: no flash\n"); /* P3 */ - return -EIO; + return -ENXIO; } if (register_blkdev(JSFD_MAJOR, "jsfd", &jsfd_fops)) { @@ -657,8 +656,6 @@ int jsfd_init(void) { return -EIO; } - printk("jsfd0: at major %d\n", MAJOR_NR); /* P3 */ - blksize_size[JSFD_MAJOR] = jsfd_blksizes; blk_size[JSFD_MAJOR] = jsfd_sizes; diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index 5f7ea6923..8004569ab 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -1605,9 +1605,9 @@ void __init keyboard_zsinit(void (*put_char)(unsigned char)) spin_unlock_irq(&sunkbd_lock); /* Register the /dev/kbd interface */ - devfs_register (NULL, "kbd", 0, DEVFS_FL_NONE, + devfs_register (NULL, "kbd", DEVFS_FL_DEFAULT, KBD_MAJOR, 0, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &kbd_fops, NULL); if (devfs_register_chrdev (KBD_MAJOR, "kbd", &kbd_fops)){ printk ("Could not register /dev/kbd device\n"); diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index dee47f98b..589b787d0 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -166,9 +166,9 @@ int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, int instance) return -EIO; sprintf (devname, "%d", instance); - dev->de = devfs_register (devfs_handle, devname, 0, DEVFS_FL_DEFAULT, + dev->de = devfs_register (devfs_handle, devname, DEVFS_FL_DEFAULT, VFC_MAJOR, instance, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &vfc_fops, NULL); return 0; } diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 67648bc75..d9f952ca6 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -243,6 +243,8 @@ #include <linux/blk.h> #include <linux/tqueue.h> #include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/smp.h> #include "sd.h" #include "scsi.h" #include "hosts.h" @@ -264,7 +266,7 @@ */ #define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a)) -#define AIC7XXX_C_VERSION "5.2.0" +#define AIC7XXX_C_VERSION "5.2.1" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -281,45 +283,12 @@ # define FALSE 0 #endif -/* - * We need the bios32.h file if we are kernel version 2.1.92 or less. The - * full set of pci_* changes wasn't in place until 2.1.93 - */ - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92) -# if defined(__sparc_v9__) || defined(__powerpc__) -# error "PPC and Sparc platforms are only support under 2.1.92 and above" -# endif -# include <linux/bios32.h> -#endif - -#if defined(__powerpc__) +#if defined(__powerpc__) || defined(__i386) # define MMAPIO -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("eieio" ::: "memory") -#elif defined(__i386__) -# define MMAPIO -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("lock ; addl $0,0(%%esp)": : :"memory") -#elif defined(__alpha__) -# ifdef mb -# undef mb -# endif -# define mb() \ - __asm__ __volatile__("mb": : :"memory") #endif -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) -# include <linux/spinlock.h> -# include <linux/smp.h> -# define cpuid smp_processor_id() # if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) +# define cpuid smp_processor_id() # define DRIVER_LOCK_INIT \ spin_lock_init(&p->spin_lock); # define DRIVER_LOCK \ @@ -337,17 +306,6 @@ # define DRIVER_LOCK # define DRIVER_UNLOCK # endif -#else -# define cpuid 0 -# define DRIVER_LOCK_INIT -# define DRIVER_LOCK \ - save_flags(cpu_flags); \ - cli(); -# define DRIVER_UNLOCK \ - restore_flags(cpu_flags); -# define le32_to_cpu(x) (x) -# define cpu_to_le32(x) (x) -#endif /* * You can try raising me if tagged queueing is enabled, or lowering @@ -359,13 +317,6 @@ #define AIC7XXX_CMDS_PER_DEVICE 8 #endif -/* Set this to the delay in seconds after SCSI bus reset. */ -#ifdef CONFIG_AIC7XXX_RESET_DELAY -#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY -#else -#define AIC7XXX_RESET_DELAY 5 -#endif - /* * Control collection of SCSI transfer statistics for the /proc filesystem. * @@ -377,28 +328,6 @@ #endif /* - * NOTE: Uncommenting the define below no longer has any effect, the - * tagged queue value array is always active now. I've added - * a setup option to set this particular array and I'm hoping - * insmod will be smart enough to set it properly as well. It's - * by use of this array that a person can enable tagged queueing. - * The DEFAULT_TAG_COMMANDS define has been changed to disable - * tagged queueing by default, so if your devices can handle tagged - * queueing you will need to add a line to their lilo.conf file like: - * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}" - * which will result in the first four devices on the first two - * controllers being set to a tagged queue depth of 32. - * - * Set this for defining the number of tagged commands on a device - * by device, and controller by controller basis. The first set - * of tagged commands will be used for the first detected aic7xxx - * controller, the second set will be used for the second detected - * aic7xxx controller, and so on. These values will *only* be used - * for targets that are tagged queueing capable; these values will - * be ignored in all other cases. The tag_commands is an array of - * 16 to allow for wide and twin adapters. Twin adapters will use - * indexes 0-7 for channel 0, and indexes 8-15 for channel 1. - * * *** Determining commands per LUN *** * * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its @@ -408,7 +337,6 @@ * use an internal queue depth of 3, with no more than one of those * three commands active at one time. */ -/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */ typedef struct { @@ -453,8 +381,8 @@ typedef struct /* * NOTE: The below structure is for reference only, the actual structure - * to modify in order to change things is located around line - * number 1305 + * to modify in order to change things is found after this fake one. + * adapter_tag_info_t aic7xxx_tag_info[] = { {DEFAULT_TAG_COMMANDS}, @@ -509,6 +437,7 @@ static const char *board_names[] = { "Adaptec AHA-394X Ultra SCSI host adapter", /* AIC_7882 */ "Adaptec AHA-398X Ultra SCSI host adapter", /* AIC_7883 */ "Adaptec AHA-2944 Ultra SCSI host adapter", /* AIC_7884 */ + "Adaptec AHA-2940UW Pro Ultra SCSI host adapter", /* AIC_7887 */ "Adaptec AIC-7895 Ultra SCSI host adapter", /* AIC_7895 */ "Adaptec AIC-7890/1 Ultra2 SCSI host adapter", /* AIC_7890 */ "Adaptec AHA-293X Ultra2 SCSI host adapter", /* AIC_7890 */ @@ -565,7 +494,7 @@ static const char *board_names[] = { * AIC-7770 I/O range to reserve for a card */ #define MINREG 0xC00 -#define MAXREG 0xCBF +#define MAXREG 0xCFF #define INTDEF 0x5C /* Interrupt Definition Register */ @@ -828,7 +757,8 @@ typedef enum { * and what flags weren't. This way, I could clean up the flag usage on * a use by use basis. Doug Ledford */ - AHC_NO_STPWR = 0x00040000, + AHC_MOTHERBOARD = 0x00020000, + AHC_NO_STPWEN = 0x00040000, AHC_RESET_DELAY = 0x00080000, AHC_A_SCANNED = 0x00100000, AHC_B_SCANNED = 0x00200000, @@ -1056,11 +986,8 @@ struct aic7xxx_host { struct timer_list dev_timer; unsigned long dev_expires[MAX_TARGETS]; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) spinlock_t spin_lock; volatile unsigned char cpu_lock_count[NR_CPUS]; -#endif - Scsi_Cmnd *dev_dtr_cmnd[MAX_TARGETS]; @@ -1384,9 +1311,7 @@ static int aic7xxx_seltime = 0x10; */ #ifdef MODULE static char * aic7xxx = NULL; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,18) MODULE_PARM(aic7xxx, "s"); -#endif /* * Just in case someone uses commas to separate items on the insmod @@ -1444,32 +1369,6 @@ static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer); * ***************************************************************************/ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) -static inline void -mdelay(int milliseconds) -{ - int i; - - for(i=0; i<milliseconds; i++) - udelay(1000); -} - -static inline int -time_after_eq(unsigned long a, unsigned long b) -{ - return((long)((a) - (b)) >= 0L); -} - -static inline int -timer_pending(struct timer_list *timer) -{ - return( timer->prev != NULL ); -} - -#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075 - -#endif - static inline unsigned char aic_inb(struct aic7xxx_host *p, long port) { @@ -1483,7 +1382,6 @@ aic_inb(struct aic7xxx_host *p, long port) { x = inb(p->base + port); } - mb(); return(x); #else return(inb(p->base + port)); @@ -1497,14 +1395,17 @@ aic_outb(struct aic7xxx_host *p, unsigned char val, long port) if(p->maddr) { writeb(val, p->maddr + port); + mb(); /* locked operation in order to force CPU ordering */ + readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */ } else { outb(val, p->base + port); + mb(); /* locked operation in order to force CPU ordering */ } - mb(); #else outb(val, p->base + port); + mb(); /* locked operation in order to force CPU ordering */ #endif } @@ -1642,7 +1543,7 @@ aic7xxx_setup(char *s) } else if (!strncmp(p, "verbose", n)) { - *(options[i].flag) = 0xff09; + *(options[i].flag) = 0xff29; } else { @@ -1669,7 +1570,7 @@ __setup("aic7xxx=", aic7xxx_setup); * is important since the sequencer can disable pausing for critical * sections. *-F*************************************************************************/ -static inline void +static void pause_sequencer(struct aic7xxx_host *p) { aic_outb(p, p->pause, HCNTRL); @@ -1677,6 +1578,10 @@ pause_sequencer(struct aic7xxx_host *p) { ; } + if(p->features & AHC_ULTRA2) + { + aic_inb(p, CCSCBCTL); + } } /*+F************************************************************************* @@ -1687,7 +1592,7 @@ pause_sequencer(struct aic7xxx_host *p) * Unpause the sequencer. Unremarkable, yet done often enough to * warrant an easy way to do it. *-F*************************************************************************/ -static inline void +static void unpause_sequencer(struct aic7xxx_host *p, int unpause_always) { if (unpause_always || @@ -1706,7 +1611,7 @@ unpause_sequencer(struct aic7xxx_host *p, int unpause_always) * Restart the sequencer program from address zero. This assumes * that the sequencer is already paused. *-F*************************************************************************/ -static inline void +static void restart_sequencer(struct aic7xxx_host *p) { aic_outb(p, 0, SEQADDR0); @@ -1787,7 +1692,6 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, struct ins_format1 *fmt1_ins; struct ins_format3 *fmt3_ins; unsigned char opcode; - volatile unsigned char hcntrl; instr = *(union ins_formats*) &seqprog[instrptr * 4]; @@ -1889,14 +1793,10 @@ aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr, } } aic_outb(p, (instr.integer & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM); - hcntrl = aic_inb(p, HCNTRL); - udelay(50); + udelay(10); break; default: @@ -2151,7 +2051,7 @@ aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period, * Use async transfers for this target */ *options = 0; - *period = 0; + *period = 255; syncrate = NULL; } return (syncrate); @@ -2950,6 +2850,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) #define WIDE_INQUIRY_BITS 0x60 #define SYNC_INQUIRY_BITS 0x10 #define SCSI_VERSION_BITS 0x07 +#define SCSI_DT_BIT 0x04 if ( (buffer[7] & WIDE_INQUIRY_BITS) && (p->features & AHC_WIDE) ) { @@ -2968,48 +2869,66 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) AHC_TRANS_CUR) ); unpause_sequencer(p, FALSE); } - if (buffer[7] & SYNC_INQUIRY_BITS) + if ( (buffer[7] & SYNC_INQUIRY_BITS) && + p->transinfo[tindex].user_offset ) { - p->needsdtr |= (1<<tindex); - p->needsdtr_copy |= (1<<tindex); - p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period; p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; - if (p->transinfo[tindex].user_offset) + if (p->features & AHC_ULTRA2) + p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; + else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) + p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; + else + p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + if ( (((buffer[2] & SCSI_VERSION_BITS) == 3) || + (buffer[56] & SCSI_DT_BIT) || + (p->dev_flags[tindex] & DEVICE_SCSI_3) ) && + (p->transinfo[tindex].user_period <= 9) && + (p->transinfo[tindex].user_options) ) { - if (p->features & AHC_ULTRA2) - p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2; - else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT) - p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT; - else - p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT; + p->needppr |= (1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->dev_flags[tindex] |= DEVICE_SCSI_3; + } + else + { + p->needsdtr |= (1<<tindex); + p->needsdtr_copy |= (1<<tindex); + p->transinfo[tindex].goal_period = + MAX(10, p->transinfo[tindex].goal_period); + p->transinfo[tindex].goal_options = 0; } } else { p->needsdtr &= ~(1<<tindex); p->needsdtr_copy &= ~(1<<tindex); - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_offset = 0; p->transinfo[tindex].goal_options = 0; } - if ( (buffer[2] & SCSI_VERSION_BITS) == 3 ) + /* + * This is needed to work around a sequencer bug for now. Regardless + * of the controller in use, if we have a Quantum drive, we need to + * limit the speed to 80MByte/sec. As soon as I get a fixed version + * of the sequencer, this code will get yanked. + */ + if(!strncmp(buffer + 8, "QUANTUM", 7) && + p->transinfo[tindex].goal_options ) { - p->dev_flags[tindex] |= DEVICE_SCSI_3; - /* - * OK, we are a SCSI 3 device and we are in need of negotiation. - * Use PPR messages instead of WDTR and SDTR messages. - */ - if ( (p->needsdtr & (1<<tindex)) || - (p->needwdtr & (1<<tindex)) ) - { - p->needppr |= (1<<tindex); - p->needppr_copy |= (1<<tindex); - } - p->needwdtr &= ~(1<<tindex); - p->needwdtr_copy &= ~(1<<tindex); - p->needsdtr &= ~(1<<tindex); - p->needsdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_period = + MAX(p->transinfo[tindex].goal_period, 10); + p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needsdtr_copy |= (1<<tindex); + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); } /* * Get the INQUIRY checksum. We use this on Ultra 160/m @@ -3039,6 +2958,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) #undef WIDE_INQUIRY_BITS #undef SYNC_INQUIRY_BITS #undef SCSI_VERSION_BITS +#undef SCSI_DT_BIT } } else if ((scb->flags & SCB_MSGOUT_BITS) != 0) @@ -4170,12 +4090,7 @@ aic7xxx_pci_intr(struct aic7xxx_host *p) { unsigned char status1; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS + 1, &status1); -#endif if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ) printk(WARN_LEAD "Data Parity Error during PCI address or PCI write" @@ -4196,12 +4111,7 @@ aic7xxx_pci_intr(struct aic7xxx_host *p) printk(WARN_LEAD "Data Parity Error has been reported via PCI pin " "PERR#\n", p->host_no, -1, -1, -1); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - PCI_STATUS + 1, status1); -#endif if (status1 & (DPR|RMA|RTA)) aic_outb(p, CLRPARERR, CLRINT); @@ -4226,11 +4136,7 @@ aic7xxx_timer(struct aic7xxx_host *p) unsigned long cpu_flags = 0; struct aic7xxx_scb *scb; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - DRIVER_LOCK -#else spin_lock_irqsave(&io_request_lock, cpu_flags); -#endif p->dev_timer_active &= ~(0x01 << MAX_TARGETS); if ( (p->dev_timer_active & (0x01 << p->scsi_id)) && time_after_eq(jiffies, p->dev_expires[p->scsi_id]) ) @@ -4287,11 +4193,7 @@ aic7xxx_timer(struct aic7xxx_host *p) } aic7xxx_run_waiting_queues(p); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95) - DRIVER_UNLOCK -#else spin_unlock_irqrestore(&io_request_lock, cpu_flags); -#endif } /*+F************************************************************************* @@ -4673,7 +4575,9 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) * As per the draft specs, any device capable of supporting any of * the option values other than 0 are not allowed to reject the * PPR message. Instead, they must negotiate out what they do - * support instead of rejecting our offering. + * support instead of rejecting our offering or else they cause + * a parity error during msg_out phase to signal that they don't + * like our settings. */ p->needppr &= ~target_mask; p->needppr_copy &= ~target_mask; @@ -4813,39 +4717,36 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) case CHECK_CONDITION: if ( !(scb->flags & SCB_SENSE) ) { - unsigned char *sense_buffer; - /* - * XXX - How do we save the residual (if there is one). - */ - if ( hscb->residual_SG_segment_count != 0 ) - aic7xxx_calculate_residual(p, scb); - /* - * Send a sense command to the requesting target. + * Send a sense command to the requesting target. * XXX - revisit this and get rid of the memcopys. - */ + */ memcpy(scb->sense_cmd, &generic_sense[0], sizeof(generic_sense)); scb->sense_cmd[1] = (cmd->lun << 5); scb->sense_cmd[4] = sizeof(cmd->sense_buffer); - sense_buffer = cmd->sense_buffer; scb->sg_list[0].length = cpu_to_le32(sizeof(cmd->sense_buffer)); + scb->sg_list[0].address = + cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer, + sizeof(cmd->sense_buffer), + PCI_DMA_FROMDEVICE)); /* * XXX - We should allow disconnection, but can't as it * might allow overlapped tagged commands. */ - /* hscb->control &= DISCENB; */ + /* hscb->control &= DISCENB; */ hscb->control = 0; hscb->target_status = 0; hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list)); - hscb->data_count = scb->sg_list[0].length; hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd)); + hscb->data_count = scb->sg_list[0].length; + hscb->data_pointer = scb->sg_list[0].address; hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]); hscb->residual_SG_segment_count = 0; hscb->residual_data_count[0] = 0; @@ -4855,53 +4756,6 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) scb->sg_count = hscb->SG_segment_count = 1; scb->sg_length = sizeof(cmd->sense_buffer); scb->tag_action = 0; - /* - * This problem could be caused if the target has lost power - * or found some other way to loose the negotiation settings, - * so if needed, we'll re-negotiate while doing the sense cmd. - * However, if this SCB already was attempting to negotiate, - * then we assume this isn't the problem and skip this part. - */ - if ( (scb->cmd->cmnd[0] != TEST_UNIT_READY) && - (p->dev_flags[tindex] & DEVICE_SCANNED) && - !(p->dtr_pending & target_mask) ) - { - p->needppr |= (p->needppr_copy & target_mask); - p->needwdtr |= (p->needwdtr_copy & target_mask); - p->needsdtr |= (p->needsdtr_copy & target_mask); - } - else if ( scb->cmd == p->dev_dtr_cmnd[tindex] ) - { - /* - * This is already a negotiation command, so we must have - * already done PPR, WDTR or SDTR. Since our negotiation - * could have gotten rejected, we don't really know the - * full state of things. Don't do anything here, and allow - * the negotiation_complete() handler to do the right - * thing. - */ - - /* - * This is the important part though. We are getting sense - * info back from this device. It's going into a fake - * command. We need to put that into the real command - * instead so that the mid level SCSI code can act upon it. - * So, when we set up these fake commands, the next pointer - * is used to point to the real command. Use that to change - * the address of our sense_buffer[] to the real command. - * However, don't do this if the real command is also a - * TEST_UNIT_READY as it will most likely pull down its own - * SENSE information anyway. - */ - if (cmd->next->cmnd[0] != TEST_UNIT_READY) - sense_buffer = cmd->next->sense_buffer; - } - scb->sg_list[0].address = - cpu_to_le32(pci_map_single(p->pdev, sense_buffer, - sizeof(cmd->sense_buffer), - PCI_DMA_FROMDEVICE)); - hscb->data_pointer = scb->sg_list[0].address; - scb->flags |= SCB_SENSE; /* * Ensure the target is busy since this will be an @@ -4924,7 +4778,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic7xxx_error(cmd) = DID_OK; break; } /* first time sense, no errors */ - aic7xxx_error(cmd) = DID_OK; + aic7xxx_error(cmd) = DID_ERROR; scb->flags &= ~SCB_SENSE; break; @@ -5171,51 +5025,14 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) } else if (scb->flags & SCB_MSGOUT_PPR) { - unsigned int max_sync, period; - unsigned char options = 0; - - if (p->features & AHC_ULTRA2) - { - if ( (aic_inb(p, SBLKCTL) & ENAB40) && - !(aic_inb(p, SSTAT2) & EXP_ACTIVE) ) - { - if( (p->features & AHC_ULTRA3) && - (p->dev_flags[tindex] & DEVICE_SCSI_3) && - (p->transinfo[tindex].goal_width == - MSG_EXT_WDTR_BUS_16_BIT) && - (p->transinfo[tindex].goal_options != 0) ) - { - max_sync = AHC_SYNCRATE_ULTRA3; - options = p->transinfo[tindex].goal_options; - } - else - { - max_sync = AHC_SYNCRATE_ULTRA2; - } - } - else - { - max_sync = AHC_SYNCRATE_ULTRA; - } - } - else if (p->features & AHC_ULTRA) - { - max_sync = AHC_SYNCRATE_ULTRA; - } - else - { - max_sync = AHC_SYNCRATE_FAST; - } - period = p->transinfo[tindex].goal_period; - aic7xxx_find_syncrate(p, &period, max_sync, &options); - p->transinfo[tindex].goal_period = period; - p->transinfo[tindex].goal_options = options; if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n", - p->host_no, CTL_OF_SCB(scb), period, + p->host_no, CTL_OF_SCB(scb), + p->transinfo[tindex].goal_period, p->transinfo[tindex].goal_offset, - p->transinfo[tindex].goal_width, options); + p->transinfo[tindex].goal_width, + p->transinfo[tindex].goal_options); } aic7xxx_construct_ppr(p, scb); } @@ -5261,8 +5078,7 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no, - CTL_OF_SCB(scb), - p->transinfo[tindex].goal_period, + CTL_OF_SCB(scb), period, p->transinfo[tindex].goal_offset); } aic7xxx_construct_sdtr(p, period, @@ -5375,15 +5191,15 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT); resid_dcnt = aic_inb(p, SCB_RESID_DCNT) | (aic_inb(p, SCB_RESID_DCNT + 1) << 8) | - (aic_inb(p, SCB_RESID_DCNT + 2) << 24); - index = scb->sg_count - resid_sgcnt; + (aic_inb(p, SCB_RESID_DCNT + 2) << 16); + index = scb->sg_count - (resid_sgcnt + 1); native_addr = le32_to_cpu(scb->sg_list[index].address); native_length = le32_to_cpu(scb->sg_list[index].length); /* * Make sure this is a valid sg_seg for the given pointer */ if(cur_addr < native_addr || - cur_addr > (native_addr + native_length)) + cur_addr > (native_addr + native_length + 1)) { printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n", p->host_no, CTL_OF_SCB(scb), cur_addr); @@ -5400,16 +5216,31 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) p->host_no, CTL_OF_SCB(scb), le32_to_cpu(scb->sg_list[index + 1].address), le32_to_cpu(scb->sg_list[index + 1].length)); + printk(WARN_LEAD " cur_address:0x%x resid_dcnt:0x%06x\n", + p->host_no, CTL_OF_SCB(scb), + cur_addr, resid_dcnt); break; } - /* - * If our current address matches the sg_seg->address then we - * have to back up the sg array to the previous segment and set - * it up to have only one byte of transfer left to go. - */ - if(cur_addr == native_addr) + if( (resid_sgcnt == 0) && + ((resid_dcnt == 0) || (resid_dcnt == 0xffffff))) + { + /* + * We are at the end of the transfer and this is about a byte + * we ignored already (because the sequencer knew this was + * the last segment and set the adapter to ignore any wide + * residue bytes that might come through, which is only done + * on the last scatter gather segment of transfers). + */ + break; + } + else if(cur_addr == native_addr) { + /* + * If our current address matches the sg_seg->address then we + * have to back up the sg array to the previous segment and set + * it up to have only one byte of transfer left to go. + */ if(index == 0) { printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been " @@ -5439,39 +5270,6 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); - /* - * The sequencer actually wants to find the new address and byte - * count in the SHCNT and SHADDR register sets. These registers - * are a shadow of the regular HCNT and HADDR registers. On the - * Ultra2 controllers, these registers are read only and the way - * we have to set their values is to put the values we want into - * the HCNT and HADDR registers and then output PRELOADEN into - * the DFCNTRL register which causes the card to latch the current - * values in the HADDR and HCNT registers and drop it through to - * the shadow registers. On older cards we copy them directly - * across by hand. - */ - if(p->features & AHC_ULTRA2) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - i=0; - udelay(1); - while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - udelay(1); - } - } - else - { - aic_outb(p, 1, STCNT); - aic_outb(p, 0, STCNT + 1); - aic_outb(p, 0, STCNT + 2); - aic_outb(p, cur_addr & 0xff, SHADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); - } } else { @@ -5491,28 +5289,46 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1); aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2); aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3); - if(p->features & AHC_ULTRA2) + } + /* + * The sequencer actually wants to find the new address and byte + * count in the SHCNT and SHADDR register sets. These registers + * are a shadow of the regular HCNT and HADDR registers. On the + * Ultra2 controllers, these registers are read only and the way + * we have to set their values is to put the values we want into + * the HCNT and HADDR registers and then output PRELOADEN into + * the DFCNTRL register which causes the card to latch the current + * values in the HADDR and HCNT registers and drop it through to + * the shadow registers. On older cards we copy them directly + * across by hand. + */ + if(p->features & AHC_ULTRA2) + { + aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - i=0; udelay(1); - while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000)) - { - aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL); - udelay(1); - } } - else + aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL); + i=0; + udelay(1); + while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000)) { - aic_outb(p, resid_dcnt & 0xff, STCNT); - aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); - aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); - aic_outb(p, cur_addr & 0xff, SHADDR); - aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); - aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); - aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + udelay(1); } } + else + { + aic_outb(p, resid_dcnt & 0xff, STCNT); + aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); + aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); + aic_outb(p, cur_addr & 0xff, SHADDR); + aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1); + aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2); + aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3); + } } break; @@ -5921,6 +5737,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) reply = TRUE; scb->flags &= ~SCB_MSGOUT_BITS; scb->flags |= SCB_MSGOUT_PPR; + p->dev_flags[tindex] |= DEVICE_SCSI_3; if (!(p->dev_flags[tindex] & DEVICE_SCANNED)) { /* @@ -5954,7 +5771,6 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) p->transinfo[tindex].user_width; p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options; - p->needppr_copy |= target_mask; } if (aic7xxx_verbose & VERBOSE_NEGOTIATION2) { @@ -5988,6 +5804,25 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) break; } } + if ( (p->transinfo[tindex].goal_period > 9) || + (p->transinfo[tindex].goal_options == 0) ) + { + scb->flags &= ~SCB_MSGOUT_BITS; + reject = TRUE; + reply = FALSE; + p->needppr &= ~(1 << tindex); + p->needppr_copy &= ~(1 << tindex); + if ( p->transinfo[tindex].goal_offset ) + { + p->needsdtr |= (1 << tindex); + p->needsdtr_copy |= (1 << tindex); + } + if ( p->transinfo[tindex].goal_width ) + { + p->needwdtr |= (1 << tindex); + p->needwdtr_copy |= (1 << tindex); + } + } } else { @@ -5995,7 +5830,7 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { default: { - reply = TRUE; + reject = TRUE; if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) || (aic7xxx_verbose > 0xffff)) ) @@ -6021,27 +5856,18 @@ aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb) } } - aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); - syncrate = aic7xxx_find_syncrate(p, &period, maxsync, - &new_trans_options); - aic7xxx_validate_offset(p, syncrate, &offset, bus_width); - aic7xxx_set_syncrate(p, syncrate, target, channel, period, - offset, new_trans_options, - AHC_TRANS_ACTIVE|AHC_TRANS_CUR); - - if( (offset != saved_offset) || - (trans_options != new_trans_options) || - ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) != - (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) ) + if ( !reject ) { aic7xxx_set_width(p, target, channel, lun, bus_width, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); + syncrate = aic7xxx_find_syncrate(p, &period, maxsync, + &new_trans_options); + aic7xxx_validate_offset(p, syncrate, &offset, bus_width); aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset, new_trans_options, - AHC_TRANS_GOAL|AHC_TRANS_QUITE); - reply = TRUE; + AHC_TRANS_ACTIVE|AHC_TRANS_CUR); } + p->dtr_pending &= ~target_mask; p->needppr &= ~target_mask; if(reply) @@ -6445,15 +6271,33 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) } } /* - * Restarting the sequencer will stop the selection and make sure devices - * are allowed to reselect in. + * Keep the sequencer from trying to restart any selections + */ + aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ); + /* + * Make sure the data bits on the bus are released + * Don't do this on 7770 chipsets, it makes them give us + * a BRKADDRINT and kills the card. */ - aic_outb(p, 0, SCSISEQ); + if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI ) + aic_outb(p, 0, SCSIBUSL); + + /* + * Delay for the selection timeout delay period then stop the selection + */ + udelay(301); aic_outb(p, CLRSELINGO, CLRSINT0); + /* + * Clear out all the interrupt status bits + */ aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1); p->flags &= ~AHC_HANDLING_REQINITS; aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1); aic_outb(p, CLRSCSIINT, CLRINT); + /* + * Restarting the sequencer will stop the selection and make sure devices + * are allowed to reselect in. + */ restart_sequencer(p); unpause_sequencer(p, TRUE); } @@ -6519,7 +6363,9 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) * A parity error has occurred during a data * transfer phase. Flag it and continue. */ - if( (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && (lastphase == P_DATAIN) ) + if( (p->features & AHC_ULTRA3) && + (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) && + (lastphase == P_DATAIN) ) { printk(WARN_LEAD "CRC error during %s phase.\n", p->host_no, CTL_OF_SCB(scb), phase); @@ -6544,13 +6390,50 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) p->host_no, CTL_OF_SCB(scb)); } } - else + else if( (lastphase == P_MESGOUT) && + (cmd == p->dev_dtr_cmnd[tindex]) && + (scb->flags & SCB_MSGOUT_PPR) ) { - printk(WARN_LEAD "Parity error during %s phase.\n", - p->host_no, CTL_OF_SCB(scb), phase); + /* + * As per the draft specs, any device capable of supporting any of + * the option values other than 0 are not allowed to reject the + * PPR message. Instead, they must negotiate out what they do + * support instead of rejecting our offering or else they cause + * a parity error during msg_out phase to signal that they don't + * like our settings. + */ + p->needppr &= ~(1 << tindex); + p->needppr_copy &= ~(1 << tindex); + aic7xxx_set_width(p, scb->cmd->target, scb->cmd->channel, scb->cmd->lun, + MSG_EXT_WDTR_BUS_8_BIT, + (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE)); + aic7xxx_set_syncrate(p, NULL, scb->cmd->target, scb->cmd->channel, 0, 0, + 0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE); + p->transinfo[tindex].goal_options = 0; + p->dtr_pending &= ~(1 << tindex); + scb->flags &= ~SCB_MSGOUT_BITS; + if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) + { + printk(INFO_LEAD "parity error during PPR message, reverting " + "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb)); + } + if ( p->transinfo[tindex].goal_width ) + { + p->needwdtr |= (1 << tindex); + p->needwdtr_copy |= (1 << tindex); + } + if ( p->transinfo[tindex].goal_offset ) + { + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->transinfo[tindex].goal_period = 10; + } + p->needsdtr |= (1 << tindex); + p->needsdtr_copy |= (1 << tindex); + } + scb = NULL; } - - if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR) + else if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR) { struct aic7xxx_syncrate *syncrate; unsigned int period = p->transinfo[tindex].cur_period; @@ -6562,6 +6445,8 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) * instead of slowing down if those exist. That's hard to do with simple * checksums though. */ + printk(WARN_LEAD "Parity error during %s phase.\n", + p->host_no, CTL_OF_SCB(scb), phase); if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL) { syncrate++; @@ -6569,20 +6454,59 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) { p->transinfo[tindex].goal_period = syncrate->period; - if( !(syncrate->sxfr_ultra2 & 0x40) ) + if( p->transinfo[tindex].goal_period > 9 ) { p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); + if (p->transinfo[tindex].goal_width) + { + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); + } + } + } + else if (p->transinfo[tindex].goal_width) + { + p->transinfo[tindex].goal_width = 0; + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = + p->transinfo[tindex].user_options; + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->needppr |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + } + else + { + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); } } else { p->transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_options = 0; + p->transinfo[tindex].goal_width = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); } - p->needppr |= (p->needppr_copy & (1<<tindex)); - p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); - p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); } p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR; } @@ -6600,6 +6524,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) if (mesg_out != MSG_NOOP) { aic_outb(p, mesg_out, MSG_OUT); + aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO); scb = NULL; } aic_outb(p, CLRSCSIPERR, CLRSINT1); @@ -6791,7 +6716,7 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) { struct aic7xxx_scb *scb = NULL; Scsi_Cmnd *cmd; - unsigned char scb_index; + unsigned char scb_index, tindex; #ifdef AIC7XXX_VERBOSE_DEBUGGING if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) @@ -6817,23 +6742,21 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) scb_index = p->qoutfifo[p->qoutfifonext]; p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; if ( scb_index >= p->scb_data->numscbs ) - scb = NULL; - else - scb = p->scb_data->scb_array[scb_index]; - if (scb == NULL) { printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, -1, -1, -1, scb_index); continue; } - else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + scb = p->scb_data->scb_array[scb_index]; + if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) { printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, (unsigned long) scb->cmd); continue; } - else if (scb->flags & SCB_QUEUED_ABORT) + tindex = TARGET_INDEX(scb->cmd); + if (scb->flags & SCB_QUEUED_ABORT) { pause_sequencer(p); if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && @@ -6856,6 +6779,43 @@ aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) */ scb->flags &= ~(SCB_ABORT|SCB_RESET); } + else if (scb->flags & SCB_SENSE) + { + char *buffer = &scb->cmd->sense_buffer[0]; + if (scb->cmd == p->dev_dtr_cmnd[tindex]) + { + struct aic7xxx_scb *old_scb; + /* + * We have valid sense data, send it back immediately. + */ + old_scb = p->scb_data->scb_array[scb->cmd->next->tag]; + *old_scb->cmd->sense_buffer = *scb->cmd->sense_buffer; + old_scb->hscb->target_status = scb->hscb->target_status; + old_scb->cmd->result = scb->hscb->target_status; + old_scb->cmd->result |= (DID_ERROR << 16); + aic7xxx_status(old_scb->cmd) = scb->hscb->target_status; + scbq_remove(&p->waiting_scbs, old_scb); + scbq_remove(&p->delayed_scbs[tindex], old_scb); + scb->cmd->next = NULL; + aic7xxx_done(p, scb); + aic7xxx_done(p, old_scb); + continue; + } + else if (buffer[12] == 0x47 || buffer[12] == 0x54) + { + /* + * SCSI errors, run domain validation and re-run negotiation + */ + p->needdv |= (1<<tindex); + /* + * Signal that we need to re-negotiate things, this also gets us our + * INQUIRY command to re-checksum off of. + */ + p->needppr |= (p->needppr_copy & (1<<tindex)); + p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); + p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); + } + } switch (status_byte(scb->hscb->target_status)) { case QUEUE_FULL: @@ -6985,6 +6945,13 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) if (intstat & SEQINT) { + /* + * Read the CCSCBCTL register to work around a bug in the Ultra2 cards + */ + if(p->features & AHC_ULTRA2) + { + aic_inb(p, CCSCBCTL); + } aic7xxx_handle_seqint(p, intstat); } @@ -7019,10 +6986,10 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) p = (struct aic7xxx_host *)dev_id; if(!p) return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95) spin_lock_irqsave(&io_request_lock, cpu_flags); if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags)) { + spin_unlock_irqrestore(&io_request_lock, cpu_flags); return; } do @@ -7033,21 +7000,6 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) aic7xxx_run_waiting_queues(p); clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags); spin_unlock_irqrestore(&io_request_lock, cpu_flags); -#else - if(set_bit(AHC_IN_ISR_BIT, (int *)&p->flags)) - { - return; - } - DRIVER_LOCK - do - { - aic7xxx_isr(irq, dev_id, regs); - } while ( (aic_inb(p, INTSTAT) & INT_PEND) ); - DRIVER_UNLOCK - aic7xxx_done_cmds_complete(p); - aic7xxx_run_waiting_queues(p); - clear_bit(AHC_IN_ISR_BIT, (int *)&p->flags); -#endif } /*+F************************************************************************* @@ -7067,7 +7019,7 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) * with queue depths for individual devices. It also allows tagged * queueing to be [en|dis]abled for a specific adapter. *-F*************************************************************************/ -static void +static int aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) { int default_depth = 3; @@ -7077,6 +7029,14 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) tindex = device->id | (device->channel << 3); target_mask = (1 << tindex); + if (p->dev_max_queue_depth[tindex] > 1) + { + /* + * We've already scanned this device, leave it alone + */ + return(p->dev_max_queue_depth[tindex]); + } + device->queue_depth = default_depth; p->dev_temp_queue_depth[tindex] = 1; p->dev_max_queue_depth[tindex] = 1; @@ -7145,6 +7105,7 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device) } } } + return(p->dev_max_queue_depth[tindex]); } /*+F************************************************************************* @@ -7172,8 +7133,7 @@ aic7xxx_select_queue_depth(struct Scsi_Host *host, { if (device->host == host) { - aic7xxx_device_queue_depth(p, device); - scbnum += device->queue_depth; + scbnum += aic7xxx_device_queue_depth(p, device); } } while (scbnum > p->scb_data->numscbs) @@ -7481,7 +7441,6 @@ release_seeprom(struct aic7xxx_host *p) */ CLOCK_PULSE(p); aic_outb(p, 0, SEECTL); - CLOCK_PULSE(p); } /*+F************************************************************************* @@ -7757,8 +7716,6 @@ write_brdctl(struct aic7xxx_host *p, unsigned char value) } } -#undef CLOCK_PULSE - /*+F************************************************************************* * Function: * aic785x_cable_detect @@ -7773,16 +7730,64 @@ aic785x_cable_detect(struct aic7xxx_host *p, int *int_50, unsigned char brdctl; aic_outb(p, BRDRW | BRDCS, BRDCTL); - udelay(1); + CLOCK_PULSE(p); aic_outb(p, 0, BRDCTL); - udelay(1); + CLOCK_PULSE(p); brdctl = aic_inb(p, BRDCTL); - udelay(1); + CLOCK_PULSE(p); *int_50 = !(brdctl & BRDDAT5); *ext_present = !(brdctl & BRDDAT6); *eeprom = (aic_inb(p, SPIOCAP) & EEPROM); } +#undef CLOCK_PULSE + +/*+F************************************************************************* + * Function: + * aic2940_uwpro_cable_detect + * + * Description: + * Detect the cables that are present on the 2940-UWPro cards + * + * NOTE: This functions assumes the SEEPROM will have already been aquired + * prior to invocation of this function. + *-F*************************************************************************/ +static void +aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68, + int *ext_68, int *eeprom) +{ + unsigned char brdctl; + + /* + * First read the status of our cables. Set the rom bank to + * 0 since the bank setting serves as a multiplexor for the + * cable detection logic. BRDDAT5 controls the bank switch. + */ + write_brdctl(p, 0); + + /* + * Now we read the state of the internal 68 connector. BRDDAT6 + * is don't care, BRDDAT7 is internal 68. The cable is + * present if the bit is 0 + */ + brdctl = read_brdctl(p); + *int_68 = !(brdctl & BRDDAT7); + + /* + * Set the bank bit in brdctl and then read the external cable state + * and the EEPROM status + */ + write_brdctl(p, BRDDAT5); + brdctl = read_brdctl(p); + + *ext_68 = !(brdctl & BRDDAT6); + *eeprom = !(brdctl & BRDDAT7); + + /* + * We're done, the calling function will release the SEEPROM for us + */ +} + /*+F************************************************************************* * Function: * aic787x_cable_detect @@ -7887,59 +7892,182 @@ configure_termination(struct aic7xxx_host *p) max_target = 8; aic_outb(p, SEEMS | SEECS, SEECTL); sxfrctl1 &= ~STPWEN; - if ( (p->adapter_control & CFAUTOTERM) || - (p->features & AHC_NEW_AUTOTERM) ) + /* + * The termination/cable detection logic is split into three distinct + * groups. Ultra2 and later controllers, 2940UW-Pro controllers, and + * older 7850, 7860, 7870, 7880, and 7895 controllers. Each has its + * own unique way of detecting their cables and writing the results + * back to the card. + */ + if (p->features & AHC_ULTRA2) { - if ( (p->adapter_control & CFAUTOTERM) && - !(p->features & AHC_NEW_AUTOTERM) ) + /* + * As long as user hasn't overridden term settings, always check the + * cable detection logic + */ + if (aic7xxx_override_term == -1) { - printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", - p->host_no); - printk(KERN_INFO "(scsi%d) Please verify driver detected settings are " - "correct.\n", p->host_no); - printk(KERN_INFO "(scsi%d) If not, then please properly set the device " - "termination\n", p->host_no); - printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting CTRL-A " - "when prompted\n", p->host_no); - printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); + aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, + &enableLVD_low, &enableLVD_high, + &eprom_present); + } + + /* + * If the user is overriding settings, then they have been preserved + * to here as fake adapter_control entries. Parse them and allow + * them to override the detected settings (if we even did detection). + */ + if (!(p->adapter_control & CFSEAUTOTERM)) + { + enableSE_low = (p->adapter_control & CFSTERM); + enableSE_high = (p->adapter_control & CFWSTERM); + } + if (!(p->adapter_control & CFAUTOTERM)) + { + enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); } - /* Configure auto termination. */ - if (p->features & AHC_NEW_AUTOTERM) + /* + * Now take those settings that we have and translate them into the + * values that must be written into the registers. + * + * Flash Enable = BRDDAT7 + * Secondary High Term Enable = BRDDAT6 + * Secondary Low Term Enable = BRDDAT5 + * LVD/Primary High Term Enable = BRDDAT4 + * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1 + */ + if (enableLVD_low != 0) { - if (aic7xxx_override_term == -1) - aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, - &enableLVD_low, &enableLVD_high, - &eprom_present); - if (!(p->adapter_control & CFSEAUTOTERM)) + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_LVD; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination " + "Enabled\n", p->host_no); + } + + if (enableLVD_high != 0) + { + brddat |= BRDDAT4; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination " + "Enabled\n", p->host_no); + } + + if (enableSE_low != 0) + { + brddat |= BRDDAT5; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Secondary Low byte termination " + "Enabled\n", p->host_no); + } + + if (enableSE_high != 0) + { + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Secondary High byte termination " + "Enabled\n", p->host_no); + } + } + else if (p->features & AHC_NEW_AUTOTERM) + { + /* + * The 50 pin connector termination is controlled by STPWEN in the + * SXFRCTL1 register. Since the Adaptec docs typically say the + * controller is not allowed to be in the middle of a cable and + * this is the only connection on that stub of the bus, there is + * no need to even check for narrow termination, it's simply + * always on. + */ + sxfrctl1 |= STPWEN; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n", + p->host_no); + + if (p->adapter_control & CFAUTOTERM) + { + aic2940_uwpro_wide_cable_detect(p, &internal68_present, + &external_present, + &eprom_present); + printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " + "Ext-68 %s)\n", p->host_no, + "Don't Care", + internal68_present ? "YES" : "NO", + external_present ? "YES" : "NO"); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, + eprom_present ? "is" : "is not"); + if (internal68_present && external_present) { - enableSE_low = (p->adapter_control & CFSTERM); - enableSE_high = (p->adapter_control & CFWSTERM); + brddat = 0; + p->flags &= ~AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n", + p->host_no); } - if (!(p->adapter_control & CFAUTOTERM)) + else { - enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM); + brddat = BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n", + p->host_no); } - internal50_present = 0; - internal68_present = 1; - external_present = 1; - } - else if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) - { - aic787x_cable_detect(p, &internal50_present, &internal68_present, - &external_present, &eprom_present); } else { - aic785x_cable_detect(p, &internal50_present, &external_present, - &eprom_present); + /* + * The termination of the Wide channel is done more like normal + * though, and the setting of this termination is done by writing + * either a 0 or 1 to BRDDAT6 of the BRDDAT register + */ + if (p->adapter_control & CFWSTERM) + { + brddat = BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n", + p->host_no); + } + else + { + brddat = 0; + } } - - if (max_target <= 8) - internal68_present = 0; - - if ( !(p->features & AHC_NEW_AUTOTERM) ) + } + else + { + if (p->adapter_control & CFAUTOTERM) { + if (p->flags & AHC_MOTHERBOARD) + { + printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", + p->host_no); + printk(KERN_INFO "(scsi%d) Please verify driver detected settings " + "are correct.\n", p->host_no); + printk(KERN_INFO "(scsi%d) If not, then please properly set the " + "device termination\n", p->host_no); + printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting " + "CTRL-A when prompted\n", p->host_no); + printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no); + } + /* Configure auto termination. */ + + if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 ) + { + aic787x_cable_detect(p, &internal50_present, &internal68_present, + &external_present, &eprom_present); + } + else + { + aic785x_cable_detect(p, &internal50_present, &external_present, + &eprom_present); + } + + if (max_target <= 8) + internal68_present = 0; + if (max_target > 8) { printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, " @@ -7955,100 +8083,73 @@ configure_termination(struct aic7xxx_host *p) internal50_present ? "YES" : "NO", external_present ? "YES" : "NO"); } - } - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, - eprom_present ? "is" : "is not"); + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no, + eprom_present ? "is" : "is not"); - /* - * Now set the termination based on what we found. BRDDAT6 - * controls wide termination enable. - * Flash Enable = BRDDAT7 - * SE High Term Enable = BRDDAT6 - * SE Low Term Enable = BRDDAT5 (7890) - * LVD High Term Enable = BRDDAT4 (7890) - */ - if ( !(p->features & AHC_NEW_AUTOTERM) && - (internal50_present && internal68_present && external_present) ) - { - printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", - p->host_no); - printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " - "in use at a time!\n", p->host_no); /* - * Force termination (low and high byte) on. This is safer than - * leaving it completely off, especially since this message comes - * most often from motherboard controllers that don't even have 3 - * connectors, but instead are failing the cable detection. + * Now set the termination based on what we found. BRDDAT6 + * controls wide termination enable. + * Flash Enable = BRDDAT7 + * SE High Term Enable = BRDDAT6 */ - internal50_present = external_present = 0; - enableSE_high = enableSE_low = 1; - } - - if ((max_target > 8) && - ((external_present == 0) || (internal68_present == 0) || - (enableSE_high != 0))) - { - brddat |= BRDDAT6; - p->flags |= AHC_TERM_ENB_SE_HIGH; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + if (internal50_present && internal68_present && external_present) + { + printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", p->host_no); - } + printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be " + "in use at a time!\n", p->host_no); + /* + * Force termination (low and high byte) on. This is safer than + * leaving it completely off, especially since this message comes + * most often from motherboard controllers that don't even have 3 + * connectors, but instead are failing the cable detection. + */ + internal50_present = external_present = 0; + enableSE_high = enableSE_low = 1; + } - if ( (((internal50_present ? 1 : 0) + - (internal68_present ? 1 : 0) + - (external_present ? 1 : 0)) <= 1) || - (enableSE_low != 0) ) - { - if (p->features & AHC_NEW_AUTOTERM) - brddat |= BRDDAT5; - else - sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_SE_LOW; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", - p->host_no); - } + if ((max_target > 8) && + ((external_present == 0) || (internal68_present == 0)) ) + { + brddat |= BRDDAT6; + p->flags |= AHC_TERM_ENB_SE_HIGH; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); + } - if (enableLVD_low != 0) - { - sxfrctl1 |= STPWEN; - p->flags |= AHC_TERM_ENB_LVD; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) LVD Low byte termination Enabled\n", - p->host_no); - } - - if (enableLVD_high != 0) - { - brddat |= BRDDAT4; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) LVD High byte termination Enabled\n", - p->host_no); + if ( ((internal50_present ? 1 : 0) + + (internal68_present ? 1 : 0) + + (external_present ? 1 : 0)) <= 1 ) + { + sxfrctl1 |= STPWEN; + p->flags |= AHC_TERM_ENB_SE_LOW; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); + } } - } - else - { - if (p->adapter_control & CFSTERM) + else /* p->adapter_control & CFAUTOTERM */ { - if (p->features & AHC_NEW_AUTOTERM) - brddat |= BRDDAT5; - else + if (p->adapter_control & CFSTERM) + { sxfrctl1 |= STPWEN; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", - p->host_no); - } + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n", + p->host_no); + } - if (p->adapter_control & CFWSTERM) - { - brddat |= BRDDAT6; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", - p->host_no); + if (p->adapter_control & CFWSTERM) + { + brddat |= BRDDAT6; + if (aic7xxx_verbose & VERBOSE_PROBE2) + printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n", + p->host_no); + } } } + aic_outb(p, sxfrctl1, SXFRCTL1); write_brdctl(p, brddat); release_seeprom(p); @@ -8236,23 +8337,8 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, } aic_outb(p, 0, SEQ_FLAGS); - /* - * We are starting to do real work on the card....it's possible we could - * generate some spurious interrupts at this point, especially in the - * event of a PCI error or some such. If there are other devices already - * registered on the same interrupt as us, this could cause the machine - * to lock up. So, we disable the interrupt this card is on until we - * finish our card setup. We only need to do this for modules, if we are - * compiled into the kernel then interrupts are already off during this - * part of the code. - */ -#ifdef MODULE - disable_irq(p->irq); -#endif - detect_maxscb(p); - printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs); if (aic7xxx_verbose & VERBOSE_PROBE2) { @@ -8277,12 +8363,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, { unsigned char devconfig; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, - DEVCONFIG, &devconfig); -#endif if ( (aic7xxx_stpwlev >> p->instance) & 0x01 ) { devconfig |= STPWLEVEL; @@ -8295,12 +8376,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, if (aic7xxx_verbose & VERBOSE_PROBE2) printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no); } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_byte(p->pdev, DEVCONFIG, devconfig); -#else - pcibios_write_config_byte(p->pci_bus, p->pci_device_fn, - DEVCONFIG, devconfig); -#endif } } #endif @@ -8404,7 +8480,7 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, * we won't have a power source for the SCSI termination, which means * we'll see infinite incoming bus resets. */ - if(p->flags & AHC_NO_STPWR) + if(p->flags & AHC_NO_STPWEN) aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1); else aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1); @@ -8484,9 +8560,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk("(scsi%d) Unable to allocate hardware SCB array; " "failing detection.\n", p->host_no); aic_outb(p, 0, SIMODE1); -#ifdef MODULE - enable_irq(p->irq); -#endif p->irq = 0; return(0); } @@ -8670,9 +8743,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring " "controller.\n", p->host_no, p->irq); aic_outb(p, 0, SIMODE1); -#ifdef MODULE - enable_irq(p->irq); -#endif p->irq = 0; return (0); } @@ -8682,10 +8752,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p, p->host_no, -1, -1 , -1); aic7xxx_clear_intstat(p); -#ifdef MODULE - enable_irq(p->irq); -#endif - unpause_sequencer(p, /* unpause_always */ TRUE); return (found); @@ -8715,10 +8781,10 @@ aic7xxx_chip_reset(struct aic7xxx_host *p) * In the future, we may call this function as a last resort for * error handling. Let's be nice and not do any unecessary delays. */ - wait = 1000; /* 1 second (1000 * 1 msec) */ + wait = 1000; /* 1 msec (1000 * 1 msec) */ while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK)) { - udelay(1); /* 1 msec */ + udelay(1); /* 1 usec */ } pause_sequencer(p); @@ -8794,7 +8860,7 @@ aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp) p->orderedtag = 0; for (i=0; i<MAX_TARGETS; i++) { - p->transinfo[i].goal_period = 0; + p->transinfo[i].goal_period = 255; p->transinfo[i].goal_offset = 0; p->transinfo[i].goal_options = 0; p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT; @@ -9369,11 +9435,7 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * PCI-bus probe. */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (pci_present()) -#else - if (pcibios_present()) -#endif { struct { @@ -9412,7 +9474,7 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_AIC7860_FE, 7, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, AHC_AIC7860_FE, 7, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860, @@ -9420,7 +9482,8 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_AIC7860_FE, 8, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 9, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, + AHC_AIC7870_FE, 9, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 10, @@ -9437,7 +9500,8 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE, 13, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 14, + AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD, + AHC_AIC7880_FE, 14, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 15, @@ -9460,106 +9524,93 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 18, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7895_FE, 19, + AHC_AIC7895_FE, 20, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 20, + AHC_AIC7890_FE, 21, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 20, + AHC_AIC7890_FE, 21, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 21, + AHC_AIC7890_FE, 22, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7890_FE, 22, + AHC_AIC7890_FE, 23, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 23, + AHC_AIC7896_FE, 24, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 24, + AHC_AIC7896_FE, 25, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7896_FE, 25, + AHC_AIC7896_FE, 26, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860, - AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWR, - AHC_AIC7860_FE, 26, + AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN, + AHC_AIC7860_FE, 27, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, - AHC_AIC7892_FE, 27, + AHC_AIC7892_FE, 28, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899, AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL, - AHC_AIC7899_FE, 28, + AHC_AIC7899_FE, 29, 32, C56_66 }, }; unsigned short command; unsigned int devconfig, i, oldverbose; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) struct pci_dev *pdev = NULL; -#else - int index; - unsigned int piobase, mmapbase; - unsigned char pci_bus, pci_devfn, pci_irq; -#endif for (i = 0; i < NUMBER(aic_pdevs); i++) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pdev = NULL; while ((pdev = pci_find_device(aic_pdevs[i].vendor_id, aic_pdevs[i].device_id, pdev))) { if (pci_enable_device(pdev)) continue; -#else - index = 0; - while (!(pcibios_find_device(aic_pdevs[i].vendor_id, - aic_pdevs[i].device_id, - index++, &pci_bus, &pci_devfn)) ) { -#endif if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */ { if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2)) @@ -9583,7 +9634,6 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Read sundry information from PCI BIOS. */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) temp_p->irq = pdev->irq; temp_p->pdev = pdev; temp_p->pci_bus = pdev->bus->number; @@ -9595,7 +9645,8 @@ aic7xxx_detect(Scsi_Host_Template *template) { if ( ((current_p->pci_bus == temp_p->pci_bus) && (current_p->pci_device_fn == temp_p->pci_device_fn)) || - (current_p->base == temp_p->base) ) + (temp_p->base && (current_p->base == temp_p->base)) || + (temp_p->mbase && (current_p->mbase == temp_p->mbase)) ) { /* duplicate PCI entry, skip it */ kfree(temp_p); @@ -9635,69 +9686,8 @@ aic7xxx_detect(Scsi_Host_Template *template) devconfig |= 0x80000040; pci_write_config_dword(pdev, DEVCONFIG, devconfig); #endif /* AIC7XXX_STRICT_PCI_SETUP */ -#else /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ - temp_p->pci_bus = pci_bus; - temp_p->pci_device_fn = pci_devfn; - pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE, - &pci_irq); - temp_p->irq = pci_irq; - pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0, - &piobase); - temp_p->base = piobase; - pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_1, - &mmapbase); - temp_p->mbase = mmapbase; - temp_p->base &= PCI_BASE_ADDRESS_IO_MASK; - temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK; - current_p = list_p; - while(current_p) - { - if ( ((current_p->pci_bus == temp_p->pci_bus) && - (current_p->pci_device_fn == temp_p->pci_device_fn)) || - (current_p->base == temp_p->base) ) - { - /* duplicate PCI entry, skip it */ - kfree(temp_p); - temp_p = NULL; - } - current_p = current_p->next; - } - if ( temp_p == NULL ) - continue; - if (aic7xxx_verbose & VERBOSE_PROBE2) - printk("aic7xxx: <%s> at PCI %d/%d/%d\n", - board_names[aic_pdevs[i].board_name_index], - temp_p->pci_bus, - PCI_SLOT(temp_p->pci_device_fn), - PCI_FUNC(temp_p->pci_device_fn)); - pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command); - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n", - (int)command); - } -#ifdef AIC7XXX_STRICT_PCI_SETUP - command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | - PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; -#else - command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO; -#endif - command &= ~PCI_COMMAND_INVALIDATE; - if (aic7xxx_pci_parity == 0) - command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); - pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND, command); -#ifdef AIC7XXX_STRICT_PCI_SETUP - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, &devconfig); - if (aic7xxx_verbose & VERBOSE_PROBE2) - { - printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig); - } - devconfig |= 0x80000040; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, devconfig); -#endif /* AIC7XXX_STRICT_PCI_SETUP */ -#endif /* LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) */ - if(check_region(temp_p->base, MAXREG - MINREG)) + if(temp_p->base && check_region(temp_p->base, MAXREG - MINREG)) { printk("aic7xxx: <%s> at PCI %d/%d/%d\n", board_names[aic_pdevs[i].board_name_index], @@ -9728,7 +9718,7 @@ aic7xxx_detect(Scsi_Host_Template *template) } #ifdef MMAPIO - if ( !(temp_p->flags & AHC_MULTI_CHANNEL) || + if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) || ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) && (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) ) { @@ -9736,11 +9726,7 @@ aic7xxx_detect(Scsi_Host_Template *template) base = temp_p->mbase & PAGE_MASK; page_offset = temp_p->mbase - base; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) temp_p->maddr = ioremap_nocache(base, page_offset + 256); -#else - temp_p->maddr = vremap(base, page_offset + 256); -#endif if(temp_p->maddr) { temp_p->maddr += page_offset; @@ -9760,12 +9746,20 @@ aic7xxx_detect(Scsi_Host_Template *template) PCI_FUNC(temp_p->pci_device_fn)); printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to " "Programmed I/O.\n"); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); -#else - vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK)); -#endif temp_p->maddr = 0; + if(temp_p->base == 0) + { + printk("aic7xxx: <%s> at PCI %d/%d/%d\n", + board_names[aic_pdevs[i].board_name_index], + temp_p->pci_bus, + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: Controller disabled by BIOS, ignoring.\n"); + kfree(temp_p); + temp_p = NULL; + continue; + } } } } @@ -9774,7 +9768,8 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Lock out other contenders for our i/o space. */ - request_region(temp_p->base, MAXREG - MINREG, "aic7xxx"); + if(temp_p->base) + request_region(temp_p->base, MAXREG - MINREG, "aic7xxx"); /* * We HAVE to make sure the first pause_sequencer() and all other @@ -9826,12 +9821,7 @@ aic7xxx_detect(Scsi_Host_Template *template) * bit of DEVCONFIG */ aic_outb(temp_p, sxfrctl1, SXFRCTL1); -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pcibios_write_config_dword(temp_p->pci_bus, temp_p->pci_device_fn, - DEVCONFIG, devconfig); -#else pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig); -#endif sxfrctl1 &= STPWEN; /* @@ -9866,7 +9856,6 @@ aic7xxx_detect(Scsi_Host_Template *template) case AHC_AIC7895: /* 7895 */ case AHC_AIC7896: /* 7896/7 */ case AHC_AIC7899: /* 7899 */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) if (PCI_FUNC(pdev->devfn) != 0) { temp_p->flags |= AHC_CHNLB; @@ -9882,25 +9871,6 @@ aic7xxx_detect(Scsi_Host_Template *template) devconfig |= SCBSIZE32; pci_write_config_dword(pdev, DEVCONFIG, devconfig); } -#else - if (PCI_FUNC(temp_p->pci_device_fn) != 0) - { - temp_p->flags |= AHC_CHNLB; - } - /* - * The 7895 is the only chipset that sets the SCBSIZE32 param - * in the DEVCONFIG register. The Ultra2 chipsets use - * the DSCOMMAND0 register instead. - */ - if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) - { - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); - devconfig |= SCBSIZE32; - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); - } -#endif break; default: break; @@ -9965,12 +9935,7 @@ aic7xxx_detect(Scsi_Host_Template *template) /* * Check the rev of the chipset before we change DSCOMMAND0 */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_dword(pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); -#endif if ((devconfig & 0xff) >= 1) { aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) | @@ -10040,12 +10005,7 @@ aic7xxx_detect(Scsi_Host_Template *template) case AHC_AIC7895: case AHC_AIC7896: case AHC_AIC7899: -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_read_config_dword(pdev, DEVCONFIG, &devconfig); -#else - pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG, - &devconfig); -#endif if (temp_p->features & AHC_ULTRA2) { if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) && @@ -10087,12 +10047,7 @@ aic7xxx_detect(Scsi_Host_Template *template) "but not enabled\n"); } } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_dword(pdev, DEVCONFIG, devconfig); -#else - pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG, - devconfig); -#endif if ( (temp_p->flags & AHC_EXTERNAL_SRAM) && (temp_p->flags & AHC_CHNLB) ) aic_outb(temp_p, 1, CCSCBBADDR); @@ -10687,20 +10642,59 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) ) { p->transinfo[tindex].goal_period = syncrate->period; - if( !(syncrate->sxfr_ultra2 & 0x40) ) + if( p->transinfo[tindex].goal_period > 9 ) { p->transinfo[tindex].goal_options = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); + if (p->transinfo[tindex].goal_width) + { + p->needwdtr |= (1<<tindex); + p->needwdtr_copy |= (1<<tindex); + } + } + } + else if (p->transinfo[tindex].goal_width) + { + p->transinfo[tindex].goal_width = 0; + p->needwdtr &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); + p->transinfo[tindex].goal_offset = + p->transinfo[tindex].user_offset; + p->transinfo[tindex].goal_period = + p->transinfo[tindex].user_period; + p->transinfo[tindex].goal_options = + p->transinfo[tindex].user_options; + if( p->transinfo[tindex].goal_period <= 9 ) + { + p->needppr |= (1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needppr_copy |= (1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + } + else + { + p->needppr &= ~(1<<tindex); + p->needsdtr |= (1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy |= (1<<tindex); } } else { p->transinfo[tindex].goal_offset = 0; - p->transinfo[tindex].goal_period = 0; + p->transinfo[tindex].goal_period = 255; p->transinfo[tindex].goal_options = 0; + p->transinfo[tindex].goal_width = 0; + p->needppr &= ~(1<<tindex); + p->needsdtr &= ~(1<<tindex); + p->needwdtr &= ~(1<<tindex); + p->needppr_copy &= ~(1<<tindex); + p->needsdtr_copy &= ~(1<<tindex); + p->needwdtr_copy &= ~(1<<tindex); } - p->needppr |= (p->needppr_copy & (1<<tindex)); - p->needsdtr |= (p->needsdtr_copy & (1<<tindex)); - p->needwdtr |= (p->needwdtr_copy & (1<<tindex)); } p->needdv &= ~(1<<tindex); } @@ -10751,8 +10745,16 @@ aic7xxx_negotiation_complete(Scsi_Cmnd *cmd) * and we didn't follow up with SDTR yet, then this will get it started. * For all other cases, this should work out to be a no-op, unless we are * doing domain validation and happen to need a new negotiation command. + * + * In case we don't want this to go any further, the cmdcmplt interrupt + * handler will NULL out the cmd->next entry so that the real SCSI command + * can be sent back to the mid layer code with SENSE data intact. We'll + * finish things up when the cmd gets sent back down to us, so no worries. */ - aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex); + if(cmd->next) + { + aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex); + } return; } @@ -10874,13 +10876,13 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, */ hscb->control = 0; scb->tag_action = 0; + cmd->tag = hscb->tag; if (p->discenable & mask) { hscb->control |= DISCENB; if ( (p->tagenable & mask) && (cmd->cmnd[0] != TEST_UNIT_READY) ) { - cmd->tag = hscb->tag; p->dev_commands_sent[tindex]++; if (p->dev_commands_sent[tindex] < 200) { @@ -11322,13 +11324,6 @@ aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd) void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - int i, mask, found, need_tag; - struct aic7xxx_scb *scb; - unsigned char qinpos, hscbp; - - found = FALSE; -#endif printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION, UTS_RELEASE); @@ -11341,121 +11336,7 @@ aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) disable_irq(p->irq); aic7xxx_print_card(p); aic7xxx_print_scratch_ram(p); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - for(i=0; i<MAX_TARGETS; i++) - { - if(p->dev_flags[i] & DEVICE_PRESENT) - { - mask = (0x01 << i); - printk(INFO_LEAD "dev_flags=0x%x, Pending:%c, PPR:%c/%c, WDTR:%c/%c, " - "SDTR:%c/%c, q_depth=%d:%d\n", - p->host_no, 0, i, 0, p->dev_flags[i], - (p->dtr_pending & mask) ? 'Y' : 'N', - (p->needppr & mask) ? 'Y' : 'N', - (p->needppr_copy & mask) ? 'Y' : 'N', - (p->needwdtr & mask) ? 'Y' : 'N', - (p->needwdtr_copy & mask) ? 'Y' : 'N', - (p->needsdtr & mask) ? 'Y' : 'N', - (p->needsdtr_copy & mask) ? 'Y' : 'N', - p->dev_active_cmds[i], - p->dev_max_queue_depth[i] ); - printk(INFO_LEAD "targ_scsirate=0x%x", p->host_no, 0, i, 0, - aic_inb(p, TARG_SCSIRATE + i)); - if (p->features & AHC_ULTRA2) - printk(", targ_offset=%d", aic_inb(p, TARG_OFFSET + i)); - printk("\n"); - } - } - /* - * Search for this command and see if we can't track it down, it's the - * one causing the timeout. Print out this command first, then all other - * active commands afterwords. - */ - need_tag = -1; - if ( cmd ) - { - scb = p->scb_data->scb_array[aic7xxx_position(cmd)]; - if ( (scb->flags & SCB_ACTIVE) && (scb->cmd == cmd) ) - { - printk("Timed out command is scb #%d:\n", scb->hscb->tag); - printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, - scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, - (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); - need_tag = scb->hscb->tag; - if (scb->flags & SCB_WAITINGQ) found=TRUE; - } - } - printk("QINFIFO: (TAG) "); - qinpos = aic_inb(p, QINPOS); - while ( qinpos != p->qinfifonext ) - { - if (p->qinfifo[qinpos] == need_tag) - found=TRUE; - printk("%d ", p->qinfifo[qinpos++]); - } - printk("\n"); - printk("Current SCB: (SCBPTR/TAG/CONTROL) %d/%d/0x%x\n", aic_inb(p, SCBPTR), - aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL) ); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - printk("WAITING_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, WAITING_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - } - printk("\n"); - printk("DISCONNECTED_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, DISCONNECTED_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - if (aic_inb(p, SCB_TAG) == need_tag) found=TRUE; - } - printk("\n"); - printk("FREE_SCBS: (SCBPTR/TAG/CONTROL) %d->", - hscbp = aic_inb(p, FREE_SCBH)); - while (hscbp != SCB_LIST_NULL) - { - aic_outb(p, hscbp, SCBPTR); - printk("%d/%d/0x%x ", hscbp, aic_inb(p, SCB_TAG), aic_inb(p, SCB_CONTROL)); - hscbp = aic_inb(p, SCB_NEXT); - } - printk("\n"); - - if (found == FALSE) - { - /* - * We haven't found the offending SCB yet, and it should be around - * somewhere, so go look for it in the cards SCBs. - */ - printk("SCBPTR CONTROL TAG NEXT\n"); - for(i=0; i<p->scb_data->maxhscbs; i++) - { - aic_outb(p, i, SCBPTR); - printk(" %3d %02x %02x %02x\n", i, - aic_inb(p, SCB_CONTROL), aic_inb(p, SCB_TAG), - aic_inb(p, SCB_NEXT)); - } - } - - - for (i=0; i < p->scb_data->numscbs; i++) - { - scb = p->scb_data->scb_array[i]; - if ( (scb->flags & SCB_ACTIVE) && (scb->cmd != cmd) ) - { - printk("Tag%d: flags=0x%x, control=0x%x, TCL=0x%x, %s\n", scb->hscb->tag, - scb->flags, scb->hscb->control, scb->hscb->target_channel_lun, - (scb->flags & SCB_WAITINGQ) ? "WAITINGQ" : "Sent" ); - } - } -#endif - sti(); + spin_unlock_irq(&io_request_lock); for(;;) barrier(); } @@ -11501,7 +11382,6 @@ aic7xxx_abort(Scsi_Cmnd *cmd) * we are following a straight code path without entering the scheduler * code. */ - pause_sequencer(p); while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR)) { @@ -12164,15 +12044,12 @@ aic7xxx_release(struct Scsi_Host *host) if(p->irq) free_irq(p->irq, p); - release_region(p->base, MAXREG - MINREG); + if(p->base) + release_region(p->base, MAXREG - MINREG); #ifdef MMAPIO if(p->maddr) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) - vfree((void *) (((unsigned long) p->maddr) & PAGE_MASK)); -#else iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK)); -#endif } #endif /* MMAPIO */ prev = NULL; @@ -12243,31 +12120,7 @@ aic7xxx_print_card(struct aic7xxx_host *p) 0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f, 0xe0, 0xf1, 0xf4, 0xfc} }, }; -#ifdef CONFIG_PCI - static struct register_ranges cards_ns[] = { - { 0, {0,} }, /* none */ - { 0, {0,} }, /* 7771 */ - { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, - 0x3c, 0x41, 0x43, 0x47} }, - { 7, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x28, 0x2b, 0x30, 0x33, - 0x3c, 0x41, 0x43, 0x47} }, - { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x33, 0x3c, 0x41} }, - { 5, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47} }, - { 5, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3} }, - { 6, {0x04, 0x08, 0x0c, 0x0e, 0x10, 0x17, 0x30, 0x34, 0x3c, 0x47, - 0xdc, 0xe3} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} }, - { 6, {0x04, 0x08, 0x0c, 0x1b, 0x30, 0x34, 0x3c, 0x43, 0xdc, 0xe3, - 0xff, 0xff} } - }; -#endif chip = p->chip & AHC_CHIPID_MASK; - /* - * Let's run through the PCI space first.... - */ printk("%s at ", board_names[p->board_name_index]); switch(p->chip & ~AHC_CHIPID_MASK) @@ -12285,38 +12138,8 @@ aic7xxx_print_card(struct aic7xxx_host *p) break; } -#ifdef CONFIG_PCI - { - unsigned char temp; - - printk("PCI Dump:\n"); - k=0; - for(i=0; i<cards_ns[chip].num_ranges; i++) - { - for(j = cards_ns[chip].range_val[ i * 2 ]; - j <= cards_ns[chip].range_val[ i * 2 + 1 ] ; - j++) - { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) - pci_read_config_byte(p->pdev, j, &temp); -#else - pcibios_read_config_byte(p->pci_bus, p->pci_device_fn, j, &temp); -#endif - printk("%02x:%02x ", j, temp); - if(++k == 13) - { - printk("\n"); - k = 0; - } - } - } - } - if(k != 0) - printk("\n"); -#endif /* CONFIG_PCI */ - /* - * Now the registers on the card.... + * the registers on the card.... */ printk("Card Dump:\n"); k = 0; @@ -12336,21 +12159,6 @@ aic7xxx_print_card(struct aic7xxx_host *p) } if(k != 0) printk("\n"); - if (p->flags & AHC_SEEPROM_FOUND) - { - unsigned short *sc1; - sc1 = (unsigned short *)&p->sc; - - printk("SEEPROM dump.\n"); - for(i=1; i<=32; i++) - { - printk("0x%04x", sc1[i-1]); - if ( (i % 8) == 0 ) - printk("\n"); - else - printk(" "); - } - } /* * If this was an Ultra2 controller, then we just hosed the card in terms diff --git a/drivers/scsi/aic7xxx.h b/drivers/scsi/aic7xxx.h index ffcedd96d..de438cac7 100644 --- a/drivers/scsi/aic7xxx.h +++ b/drivers/scsi/aic7xxx.h @@ -23,21 +23,7 @@ #ifndef _aic7xxx_h #define _aic7xxx_h -#define AIC7XXX_H_VERSION "3.2.4" - -#ifndef LINUX_VERSION_CODE -#include <linux/version.h> -#endif - -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z)) -#endif - -#if defined(__i386__) -# define AIC7XXX_BIOSPARAM aic7xxx_biosparam -#else -# define AIC7XXX_BIOSPARAM NULL -#endif +#define AIC7XXX_H_VERSION "5.2.0" /* * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields @@ -61,7 +47,7 @@ abort: aic7xxx_abort, \ reset: aic7xxx_reset, \ slave_attach: NULL, \ - bios_param: AIC7XXX_BIOSPARAM, \ + bios_param: aic7xxx_biosparam, \ can_queue: 255, /* max simultaneous cmds */\ this_id: -1, /* scsi id of host adapter */\ sg_tablesize: 0, /* max scatter-gather cmds */\ diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq index de3afbf92..15c887167 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.seq +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -60,11 +60,7 @@ reset: clr SCSISIGO; /* De-assert BSY */ and SXFRCTL1, ~BITBUCKET; /* Always allow reselection */ - if ((p->flags & AHC_TARGETMODE) != 0) { - mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP; - } else { - mvi SCSISEQ, ENRSELI|ENAUTOATNP; - } + mvi SCSISEQ, ENRSELI|ENAUTOATNP; if ((p->features & AHC_CMD_CHAN) != 0) { /* Ensure that no DMA operations are in progress */ @@ -182,6 +178,15 @@ initialize_scsiid: and SCSIID, OID; /* Clear old target */ or SCSIID, A; } + mov SCSIDATL, ALLZEROS; /* clear out the latched */ + /* data register, this */ + /* fixes a bug on some */ + /* controllers where the */ + /* last byte written to */ + /* this register can leak */ + /* onto the data bus at */ + /* bad times, such as during */ + /* selection timeouts */ mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret; /* @@ -227,118 +232,6 @@ ndx_dtr_2: selection: test SSTAT0,SELDO jnz select_out; -select_in: - if ((p->flags & AHC_TARGETMODE) != 0) { - test SSTAT0, TARGET jz initiator_reselect; - /* - * We've just been selected. Assert BSY and - * setup the phase for receiving the messages - * from the target. - */ - mvi SCSISIGO, P_MESGOUT|BSYO; - mvi CLRSINT0, CLRSELDO; - - /* - * If ATN isn't asserted, go directly to bus free. - */ - test SCSISIGI, ATNI jz target_busfree; - - /* - * Setup the DMA for sending the identify and - * command information. - */ - mov A, TMODE_CMDADDR_NEXT; - mvi DINDEX, HADDR; - mvi TMODE_CMDADDR call set_32byte_addr; - mvi DFCNTRL, FIFORESET; - - clr SINDEX; - /* Watch ATN closely now */ -message_loop: - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - and SXFRCTL0, ~SPIOEN; - mov DINDEX, SCSIDATL; - mov DFDAT, DINDEX; - inc SINDEX; - - /* Message Testing... */ - test DINDEX, MSG_IDENTIFYFLAG jz . + 2; - mov ARG_1, DINDEX; - - test SCSISIGI, ATNI jnz message_loop; - add A, -4, SINDEX; - jc target_cmdphase; - mvi DFDAT, SCB_LIST_NULL; /* Terminate the message list */ - -target_cmdphase: - add HCNT[0], 1, A; - clr HCNT[1]; - clr HCNT[2]; - mvi SCSISIGO, P_COMMAND|BSYO; - or SXFRCTL0, SPIOEN; - test SSTAT0, SPIORDY jz .; - mov A, SCSIDATL; - mov DFDAT, A; /* Store for host */ - - /* - * Determine the number of bytes to read - * based on the command group code. Count is - * one less than the total since we've already - * fetched the first byte. - */ - clr SINDEX; - shr A, CMD_GROUP_CODE_SHIFT; - add SEQADDR0, A; - - add SINDEX, CMD_GROUP0_BYTE_DELTA; - nop; /* Group 1 and 2 are the same */ - add SINDEX, CMD_GROUP2_BYTE_DELTA; - nop; /* Group 3 is reserved */ - add SINDEX, CMD_GROUP4_BYTE_DELTA; - add SINDEX, CMD_GROUP5_BYTE_DELTA; - /* Group 6 and 7 are not handled yet */ - - mov A, SINDEX; - add HCNT[0], A; - -command_loop: - test SSTAT0, SPIORDY jz .; - cmp SINDEX, 1 jne . + 2; - and SXFRCTL0, ~SPIOEN; /* Last Byte */ - mov DFDAT, SCSIDATL; - dec SINDEX; - test SINDEX, 0xFF jnz command_loop; - - or DFCNTRL, HDMAEN|FIFOFLUSH; - - call dma_finish; - - test ARG_1, MSG_IDENTIFY_DISCFLAG jz selectin_post; - - mvi SCSISIGO, P_MESGIN|BSYO; - - or SXFRCTL0, SPIOEN; - - mvi MSG_DISCONNECT call target_outb; - -selectin_post: - inc TMODE_CMDADDR_NEXT; - cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2; - clr TMODE_CMDADDR_NEXT; - mvi QOUTFIFO, SCB_LIST_NULL; - mvi INTSTAT,CMDCMPLT; - - test ARG_1, MSG_IDENTIFY_DISCFLAG jnz target_busfree; - - /* Busy loop on something then go to data or status phase */ - -target_busfree: - clr SCSISIGO; - jmp poll_for_work; - - } - /* * Reselection has been initiated by a target. Make a note that we've been * reselected, but haven't seen an IDENTIFY message from the target yet. @@ -444,13 +337,14 @@ clear_target_state: * STCNT may have been cleared, so restore it from the residual field. */ data_phase_reinit: - if ((p->features & AHC_CMD_CHAN) != 0) { - if ((p->features & AHC_ULTRA2) != 0) { - bmov HADDR, SHADDR, 4; - bmov HCNT, SCB_RESID_DCNT, 3; - } + if ((p->features & AHC_ULTRA2) != 0) { + bmov HADDR, SHADDR, 4; + bmov HCNT, SCB_RESID_DCNT, 3; + } + if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) { bmov STCNT, SCB_RESID_DCNT, 3; - } else { + } + if ((p->features & AHC_CMD_CHAN) == 0) { mvi DINDEX, STCNT; mvi SCB_RESID_DCNT call bcopy_3; } @@ -677,10 +571,13 @@ ultra2_dmafifoflush: test DFSTATUS, FIFOEMP jz ultra2_dmafifoflush; test DFSTATUS, MREQPEND jnz .; ultra2_dmahalt: - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - and DFCNTRL, ~SCSIEN; - test DFCNTRL, SCSIEN jnz .; + test SCSIOFFSET, 0x7f jnz ultra2_shutdown; +ultra2_await_nreq: + test SCSISIGI, REQI jz ultra2_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz ultra2_await_nreq; +ultra2_shutdown: + and DFCNTRL, ~(HDMAEN|SCSIEN); + test DFCNTRL, (HDMAEN|SCSIEN) jnz .; bmov SCB_RESID_DCNT, STCNT, 3; mov SCB_RESID_SGCNT, SG_COUNT; or SXFRCTL0, CLRSTCNT|CLRCHN; @@ -719,10 +616,11 @@ p_command_dma_loop: test SSTAT0, SDONE jnz p_command_ultra2_dma_done; test SSTAT1,PHASEMIS jz p_command_dma_loop; /* ie. underrun */ p_command_ultra2_dma_done: - and DFCNTRL, ~HDMAEN; - test DFCNTRL, HDMAEN jnz .; - and DFCNTRL, ~SCSIEN; - test DFCNTRL, SCSIEN jnz .; + test SCSISIGI, REQI jz p_command_ultra2_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz p_command_ultra2_dma_done; +p_command_ultra2_shutdown: + and DFCNTRL, ~(HDMAEN|SCSIEN); + test DFCNTRL, (HDMAEN|SCSIEN) jnz .; or SXFRCTL0, CLRSTCNT|CLRCHN; } jmp ITloop; @@ -1069,25 +967,12 @@ mesgin_wide_residue: mvi ARG_1 call inb_next; /* ACK the wide_residue and get */ /* the size byte */ /* - * See if we'll ignore this wide residue (because it's an overrun byte) - */ - if ((p->features & AHC_ULTRA2) != 0) { - test SSTAT2, WIDE_RES jnz mesgin_done; - } else { - test SCB_RESID_SGCNT,0xff jnz wide_residue_int; - test SCB_RESID_DCNT[0],0xff jnz wide_residue_int; - test SCB_RESID_DCNT[1],0xff jnz wide_residue_int; - test SCB_RESID_DCNT[2],0xff jnz wide_residue_int; - jmp mesgin_done; - } -wide_residue_int: -/* * In order for this to be reliable, we have to do all sorts of horrible * magic in terms of resetting the datafifo and reloading the shadow layer * with the correct new values (so that a subsequent save data pointers * message will do the right thing). We let the kernel do that work. */ - mvi INTSTAT,WIDE_RESIDUE; + mvi INTSTAT, WIDE_RESIDUE; jmp mesgin_done; /* @@ -1136,17 +1021,6 @@ inb_first: inb_last: mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/ -if ((p->flags & AHC_TARGETMODE) != 0) { - /* - * Send a byte to an initiator in Automatic PIO mode. - * SPIOEN must be on prior to calling this routine. - */ -target_outb: - mov SCSIDATL, SINDEX; - test SSTAT0, SPIORDY jz .; - ret; -} - mesgin_phasemis: /* * We expected to receive another byte, but the target changed phase @@ -1191,6 +1065,12 @@ dma_fifoempty: * actually off first lest we get an ILLSADDR. */ dma_dmadone: + cmp LASTPHASE, P_COMMAND je dma_await_nreq; + test SCSIRATE, 0x0f jnz dma_shutdown; +dma_await_nreq: + test SCSISIGI, REQI jz dma_shutdown; + test SSTAT1, (PHASEMIS|REQINIT) jz dma_await_nreq; +dma_shutdown: and DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN); dma_halt: /* diff --git a/drivers/scsi/aic7xxx_proc.c b/drivers/scsi/aic7xxx_proc.c index ec80af90d..7320eb6b6 100644 --- a/drivers/scsi/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx_proc.c @@ -172,7 +172,6 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, #else size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n"); #endif - size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY); size += sprintf(BLS, "\n"); size += sprintf(BLS, "Adapter Configuration:\n"); size += sprintf(BLS, " SCSI Adapter: %s\n", diff --git a/drivers/scsi/aic7xxx_seq.c b/drivers/scsi/aic7xxx_seq.c index 511bfcb73..047a34de0 100644 --- a/drivers/scsi/aic7xxx_seq.c +++ b/drivers/scsi/aic7xxx_seq.c @@ -4,34 +4,33 @@ static unsigned char seqprog[] = { 0xff, 0x6a, 0x06, 0x08, 0x7f, 0x02, 0x04, 0x08, - 0x32, 0x6a, 0x00, 0x00, 0x12, 0x6a, 0x00, 0x00, 0xff, 0x6a, 0xd6, 0x09, 0xff, 0x6a, 0xdc, 0x09, - 0x00, 0x65, 0x42, 0x59, + 0x00, 0x65, 0xca, 0x58, 0xf7, 0x01, 0x02, 0x08, 0xff, 0x4e, 0xc8, 0x08, 0xbf, 0x60, 0xc0, 0x08, 0x60, 0x0b, 0x86, 0x68, - 0x40, 0x00, 0x0e, 0x68, + 0x40, 0x00, 0x0c, 0x68, 0x08, 0x1f, 0x3e, 0x10, 0x60, 0x0b, 0x86, 0x68, - 0x40, 0x00, 0x0e, 0x68, + 0x40, 0x00, 0x0c, 0x68, 0x08, 0x1f, 0x3e, 0x10, - 0xff, 0x3e, 0x4a, 0x60, - 0x40, 0xfa, 0x12, 0x78, + 0xff, 0x3e, 0x48, 0x60, + 0x40, 0xfa, 0x10, 0x78, 0xff, 0xf6, 0xd4, 0x08, 0x01, 0x4e, 0x9c, 0x18, 0x40, 0x60, 0xc0, 0x00, - 0x00, 0x4d, 0x12, 0x70, + 0x00, 0x4d, 0x10, 0x70, 0x01, 0x4e, 0x9c, 0x18, 0xbf, 0x60, 0xc0, 0x08, - 0x00, 0x6a, 0xbe, 0x5c, + 0x00, 0x6a, 0x3e, 0x5c, 0xff, 0x4e, 0xc8, 0x18, - 0x02, 0x6a, 0xd4, 0x5b, + 0x02, 0x6a, 0x54, 0x5b, 0xff, 0x52, 0x20, 0x09, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x52, 0x4a, 0x5c, + 0x00, 0x52, 0xca, 0x5b, 0x03, 0xb0, 0x52, 0x31, 0xff, 0xb0, 0x52, 0x09, 0xff, 0xb1, 0x54, 0x09, @@ -40,8 +39,8 @@ static unsigned char seqprog[] = { 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, 0xff, 0x3e, 0x20, 0x09, - 0x00, 0x65, 0x50, 0x58, - 0x00, 0x65, 0x0e, 0x40, + 0x00, 0x65, 0x4e, 0x58, + 0x00, 0x65, 0x0c, 0x40, 0xf7, 0x1f, 0xca, 0x08, 0x08, 0xa1, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, @@ -52,6 +51,7 @@ static unsigned char seqprog[] = { 0xf0, 0xa1, 0xc8, 0x08, 0x0f, 0x05, 0x0a, 0x08, 0x00, 0x05, 0x0a, 0x00, + 0xff, 0x6a, 0x0c, 0x08, 0x5a, 0x6a, 0x00, 0x04, 0x12, 0x65, 0x02, 0x00, 0x31, 0x6a, 0xca, 0x00, @@ -69,74 +69,14 @@ static unsigned char seqprog[] = { 0xff, 0x6c, 0x0a, 0x08, 0x20, 0x64, 0xca, 0x18, 0xff, 0x6c, 0x08, 0x0c, - 0x40, 0x0b, 0x0e, 0x69, - 0x80, 0x0b, 0x00, 0x79, - 0xa4, 0x6a, 0x06, 0x00, - 0x40, 0x6a, 0x16, 0x00, - 0x10, 0x03, 0xfc, 0x78, - 0xff, 0x50, 0xc8, 0x08, - 0x88, 0x6a, 0xcc, 0x00, - 0x49, 0x6a, 0x3a, 0x5c, - 0x01, 0x6a, 0x26, 0x01, - 0xff, 0x6a, 0xca, 0x08, - 0x08, 0x01, 0x02, 0x00, - 0x02, 0x0b, 0x9c, 0x78, - 0xf7, 0x01, 0x02, 0x08, - 0xff, 0x06, 0xcc, 0x08, - 0xff, 0x66, 0x32, 0x09, - 0x01, 0x65, 0xca, 0x18, - 0x80, 0x66, 0xaa, 0x78, - 0xff, 0x66, 0xa2, 0x08, - 0x10, 0x03, 0x9a, 0x68, - 0xfc, 0x65, 0xc8, 0x18, - 0x00, 0x65, 0xb2, 0x48, - 0xff, 0x6a, 0x32, 0x01, - 0x01, 0x64, 0x18, 0x19, - 0xff, 0x6a, 0x1a, 0x09, - 0xff, 0x6a, 0x1c, 0x09, - 0x84, 0x6a, 0x06, 0x00, - 0x08, 0x01, 0x02, 0x00, - 0x02, 0x0b, 0xbc, 0x78, - 0xff, 0x06, 0xc8, 0x08, - 0xff, 0x64, 0x32, 0x09, - 0xff, 0x6a, 0xca, 0x08, - 0x5b, 0x64, 0xc8, 0x28, - 0x00, 0x62, 0xc4, 0x18, - 0xfc, 0x65, 0xca, 0x18, - 0xff, 0x6a, 0xd4, 0x08, - 0xfa, 0x65, 0xca, 0x18, - 0xff, 0x6a, 0xd4, 0x08, - 0x04, 0x65, 0xca, 0x18, - 0x0b, 0x65, 0xca, 0x18, - 0xff, 0x65, 0xc8, 0x08, - 0x00, 0x8c, 0x18, 0x19, - 0x02, 0x0b, 0xd8, 0x78, - 0x01, 0x65, 0xde, 0x60, - 0xf7, 0x01, 0x02, 0x08, - 0xff, 0x06, 0x32, 0x09, - 0xff, 0x65, 0xca, 0x18, - 0xff, 0x65, 0xd8, 0x68, - 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x40, 0x51, 0xf0, 0x78, - 0xe4, 0x6a, 0x06, 0x00, - 0x08, 0x01, 0x02, 0x00, - 0x04, 0x6a, 0x6c, 0x5b, - 0x01, 0x50, 0xa0, 0x18, - 0x00, 0x50, 0xf6, 0xe0, - 0xff, 0x6a, 0xa0, 0x08, - 0xff, 0x6a, 0x3a, 0x01, - 0x02, 0x6a, 0x22, 0x01, - 0x40, 0x51, 0xfc, 0x68, - 0xff, 0x6a, 0x06, 0x08, - 0x00, 0x65, 0x0e, 0x40, + 0x40, 0x0b, 0x96, 0x68, 0x20, 0x6a, 0x16, 0x00, 0xf0, 0x19, 0x6e, 0x08, 0x08, 0x6a, 0x18, 0x00, 0x08, 0x11, 0x22, 0x00, 0x08, 0x6a, 0x66, 0x58, 0x08, 0x6a, 0x68, 0x00, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x12, 0x6a, 0x00, 0x00, 0x40, 0x6a, 0x16, 0x00, 0xff, 0x3e, 0x20, 0x09, @@ -147,21 +87,21 @@ static unsigned char seqprog[] = { 0x08, 0x6a, 0x66, 0x58, 0x80, 0x6a, 0x68, 0x00, 0x80, 0x36, 0x6c, 0x00, - 0x00, 0x65, 0x1e, 0x5c, + 0x00, 0x65, 0x9e, 0x5b, 0xff, 0x3d, 0xc8, 0x08, - 0xbf, 0x64, 0x5a, 0x79, - 0x80, 0x64, 0x22, 0x72, - 0xa0, 0x64, 0x52, 0x72, - 0xc0, 0x64, 0x4a, 0x72, - 0xe0, 0x64, 0x92, 0x72, + 0xbf, 0x64, 0xe2, 0x78, + 0x80, 0x64, 0xac, 0x71, + 0xa0, 0x64, 0xdc, 0x71, + 0xc0, 0x64, 0xd4, 0x71, + 0xe0, 0x64, 0x1c, 0x72, 0x01, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0xf7, 0x11, 0x22, 0x08, - 0x00, 0x65, 0x42, 0x59, + 0x00, 0x65, 0xca, 0x58, 0xff, 0x06, 0xd4, 0x08, 0xf7, 0x01, 0x02, 0x08, - 0x09, 0x0c, 0x3c, 0x79, - 0x08, 0x0c, 0x0e, 0x68, + 0x09, 0x0c, 0xc4, 0x78, + 0x08, 0x0c, 0x0c, 0x68, 0x01, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0x26, 0x09, 0x02, 0x6a, 0x08, 0x30, @@ -173,25 +113,25 @@ static unsigned char seqprog[] = { 0x03, 0xa9, 0x18, 0x31, 0x03, 0xa9, 0x10, 0x30, 0x08, 0x6a, 0xcc, 0x00, - 0xa9, 0x6a, 0x34, 0x5c, - 0x00, 0x65, 0x7a, 0x41, + 0xa9, 0x6a, 0xb4, 0x5b, + 0x00, 0x65, 0x02, 0x41, 0xa8, 0x6a, 0x6a, 0x00, 0x79, 0x6a, 0x6a, 0x00, - 0x40, 0x3d, 0x62, 0x69, + 0x40, 0x3d, 0xea, 0x68, 0x04, 0x35, 0x6a, 0x00, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0x0e, 0x5b, 0x80, 0x6a, 0xd4, 0x01, - 0x10, 0x36, 0x4e, 0x69, + 0x10, 0x36, 0xd6, 0x68, 0x10, 0x36, 0x6c, 0x00, 0x07, 0xac, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x05, 0xa3, 0x70, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0x2c, 0x5c, - 0x00, 0x65, 0x26, 0x5c, + 0xac, 0x6a, 0xac, 0x5b, + 0x00, 0x65, 0xa6, 0x5b, 0x38, 0x6a, 0xcc, 0x00, - 0xa3, 0x6a, 0x30, 0x5c, - 0xff, 0x38, 0x8a, 0x69, + 0xa3, 0x6a, 0xb0, 0x5b, + 0xff, 0x38, 0x12, 0x69, 0x80, 0x02, 0x04, 0x00, 0xe7, 0x35, 0x6a, 0x08, 0x03, 0x69, 0x18, 0x31, @@ -199,338 +139,334 @@ static unsigned char seqprog[] = { 0xff, 0x6a, 0x10, 0x00, 0xff, 0x6a, 0x12, 0x00, 0xff, 0x6a, 0x14, 0x00, - 0x01, 0x38, 0x90, 0x61, + 0x01, 0x38, 0x18, 0x61, 0xbf, 0x35, 0x6a, 0x08, 0x02, 0x6a, 0xf8, 0x01, 0xff, 0x69, 0xca, 0x08, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0x94, 0x69, - 0x04, 0x0b, 0xa0, 0x69, - 0x10, 0x0c, 0x96, 0x79, - 0x04, 0x0b, 0xa0, 0x69, + 0x04, 0x0b, 0x1c, 0x69, + 0x04, 0x0b, 0x28, 0x69, + 0x10, 0x0c, 0x1e, 0x79, + 0x04, 0x0b, 0x28, 0x69, 0xff, 0x6a, 0xca, 0x08, - 0x00, 0x35, 0x76, 0x5b, - 0x80, 0x02, 0xf4, 0x69, - 0xff, 0x65, 0xe4, 0x79, + 0x00, 0x35, 0xee, 0x5a, + 0x80, 0x02, 0x7c, 0x69, + 0xff, 0x65, 0x6c, 0x79, 0xff, 0x38, 0x70, 0x18, - 0xff, 0x38, 0xe4, 0x79, - 0x80, 0xea, 0xc0, 0x61, + 0xff, 0x38, 0x6c, 0x79, + 0x80, 0xea, 0x48, 0x61, 0xef, 0x38, 0xc8, 0x18, 0x80, 0x6a, 0xc8, 0x00, - 0x00, 0x65, 0xb2, 0x49, + 0x00, 0x65, 0x3a, 0x49, 0x33, 0x38, 0xc8, 0x28, 0xff, 0x64, 0xd0, 0x09, 0x04, 0x39, 0xc0, 0x31, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xb8, 0x79, + 0x80, 0xeb, 0x40, 0x79, 0xf7, 0xeb, 0xd6, 0x09, - 0x08, 0xeb, 0xbc, 0x69, + 0x08, 0xeb, 0x44, 0x69, 0x01, 0x6a, 0xd6, 0x01, 0x08, 0xe9, 0x10, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0x39, 0x6a, 0x32, 0x5c, + 0x39, 0x6a, 0xb2, 0x5b, 0x08, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x88, 0x6a, 0xa0, 0x5c, - 0x00, 0x65, 0x26, 0x5c, + 0x00, 0x65, 0x30, 0x5c, + 0x88, 0x6a, 0x20, 0x5c, + 0x00, 0x65, 0xa6, 0x5b, 0xff, 0x6a, 0xc8, 0x08, 0x08, 0x39, 0x72, 0x18, 0x00, 0x3a, 0x74, 0x20, - 0x01, 0x0c, 0xdc, 0x79, - 0x10, 0x0c, 0x7a, 0x79, + 0x01, 0x0c, 0x64, 0x79, + 0x10, 0x0c, 0x02, 0x79, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x0b, 0xe2, 0x69, - 0x00, 0x65, 0xfc, 0x59, + 0x04, 0x0b, 0x6a, 0x69, + 0x00, 0x65, 0x84, 0x59, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0xff, 0x08, 0x52, 0x09, 0xff, 0x09, 0x54, 0x09, 0xff, 0x0a, 0x56, 0x09, 0xff, 0x38, 0x50, 0x09, - 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0xfc, 0x59, + 0x00, 0x65, 0xaa, 0x40, + 0x00, 0x65, 0x84, 0x59, 0x7f, 0x02, 0x04, 0x08, 0xe1, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, - 0x04, 0x93, 0x12, 0x6a, + 0x00, 0x65, 0xaa, 0x40, + 0x04, 0x93, 0x9a, 0x69, 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x00, 0x6a, + 0x20, 0x93, 0x88, 0x69, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x01, 0x94, 0x02, 0x7a, - 0x10, 0x94, 0x10, 0x6a, - 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x14, 0x6a, - 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x18, 0x6a, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x01, 0x94, 0x8a, 0x79, + 0x10, 0x94, 0x98, 0x69, + 0x7f, 0x05, 0xa0, 0x69, + 0x02, 0x03, 0xa0, 0x79, + 0x11, 0x0c, 0x9c, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x28, 0x93, 0xa2, 0x69, 0x03, 0x08, 0x52, 0x31, 0xff, 0x38, 0x50, 0x09, 0x12, 0x01, 0x02, 0x00, 0xff, 0x6a, 0xd4, 0x0c, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0x0e, 0x5b, 0x05, 0xb4, 0x10, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x03, 0x8c, 0x10, 0x30, 0x88, 0x6a, 0xcc, 0x00, - 0xb4, 0x6a, 0x30, 0x5c, + 0xb4, 0x6a, 0xb0, 0x5b, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, - 0x00, 0x65, 0x26, 0x5c, - 0x3d, 0x6a, 0x76, 0x5b, + 0x00, 0x65, 0xa6, 0x5b, + 0x3d, 0x6a, 0xee, 0x5a, 0xac, 0x6a, 0x26, 0x01, - 0x04, 0x0b, 0x38, 0x6a, - 0x04, 0x0b, 0x3e, 0x6a, - 0x10, 0x0c, 0x3a, 0x7a, - 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x40, 0x6a, - 0xdf, 0x93, 0x26, 0x09, - 0x20, 0x93, 0x44, 0x6a, + 0x04, 0x0b, 0xc2, 0x69, + 0x04, 0x0b, 0xc8, 0x69, + 0x10, 0x0c, 0xc4, 0x79, + 0x02, 0x03, 0xcc, 0x79, + 0x11, 0x0c, 0xc8, 0x79, + 0xd7, 0x93, 0x26, 0x09, + 0x28, 0x93, 0xce, 0x69, 0x12, 0x01, 0x02, 0x00, - 0x00, 0x65, 0x22, 0x41, - 0x00, 0x65, 0x8e, 0x5b, + 0x00, 0x65, 0xaa, 0x40, + 0x00, 0x65, 0x0e, 0x5b, 0xff, 0x06, 0x44, 0x09, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x34, 0xca, 0x08, - 0x80, 0x65, 0x76, 0x62, + 0x80, 0x65, 0x00, 0x62, 0x0f, 0xa1, 0xca, 0x08, 0x07, 0xa1, 0xca, 0x08, 0x40, 0xa0, 0xc8, 0x08, 0x00, 0x65, 0xca, 0x00, 0x80, 0x65, 0xca, 0x00, - 0x80, 0xa0, 0x66, 0x7a, + 0x80, 0xa0, 0xf0, 0x79, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x78, 0x42, - 0x20, 0xa0, 0x7e, 0x7a, + 0x00, 0x65, 0x02, 0x42, + 0x20, 0xa0, 0x08, 0x7a, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x86, 0x62, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0x10, 0x62, 0x23, 0xa0, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x86, 0x62, - 0x00, 0xb9, 0x7e, 0x42, - 0xff, 0x65, 0x7e, 0x62, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0x10, 0x62, + 0x00, 0xb9, 0x08, 0x42, + 0xff, 0x65, 0x08, 0x62, 0xa1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x10, 0x51, 0x86, 0x72, + 0x10, 0x51, 0x10, 0x72, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x65, 0x0c, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xa0, 0x3d, 0x50, 0x72, + 0x00, 0x65, 0x9e, 0x5b, + 0xa0, 0x3d, 0xda, 0x71, 0x40, 0x6a, 0x18, 0x00, 0xff, 0x34, 0xa6, 0x08, - 0x80, 0x34, 0x8e, 0x62, + 0x80, 0x34, 0x18, 0x62, 0x7f, 0xa0, 0x40, 0x09, 0x08, 0x6a, 0x68, 0x00, - 0x00, 0x65, 0x22, 0x41, - 0x64, 0x6a, 0x66, 0x5b, - 0x80, 0x64, 0x04, 0x6b, - 0x04, 0x64, 0xe6, 0x72, - 0x02, 0x64, 0xec, 0x72, - 0x00, 0x6a, 0xae, 0x72, - 0x03, 0x64, 0x00, 0x73, - 0x01, 0x64, 0xe2, 0x72, - 0x07, 0x64, 0x42, 0x73, - 0x08, 0x64, 0xaa, 0x72, - 0x23, 0x64, 0x46, 0x73, + 0x00, 0x65, 0xaa, 0x40, + 0x64, 0x6a, 0xe4, 0x5a, + 0x80, 0x64, 0x8e, 0x6a, + 0x04, 0x64, 0x70, 0x72, + 0x02, 0x64, 0x76, 0x72, + 0x00, 0x6a, 0x38, 0x72, + 0x03, 0x64, 0x8a, 0x72, + 0x01, 0x64, 0x6c, 0x72, + 0x07, 0x64, 0xcc, 0x72, + 0x08, 0x64, 0x34, 0x72, + 0x23, 0x64, 0xd0, 0x72, 0x11, 0x6a, 0x22, 0x01, - 0x07, 0x6a, 0x58, 0x5b, + 0x07, 0x6a, 0xd6, 0x5a, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x22, 0x41, - 0xff, 0xa8, 0xb2, 0x6a, - 0xff, 0xa2, 0xca, 0x7a, + 0x00, 0x65, 0xaa, 0x40, + 0xff, 0xa8, 0x3c, 0x6a, + 0xff, 0xa2, 0x54, 0x7a, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, - 0xff, 0xa2, 0xca, 0x7a, + 0x00, 0xb9, 0xca, 0x5b, + 0xff, 0xa2, 0x54, 0x7a, 0x71, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, - 0x40, 0x51, 0xca, 0x62, + 0x40, 0x51, 0x54, 0x62, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, + 0x00, 0xb9, 0xca, 0x5b, 0xff, 0x3e, 0x74, 0x09, 0xff, 0x90, 0x7c, 0x08, - 0x00, 0x65, 0x50, 0x58, - 0x00, 0x65, 0x34, 0x41, - 0x20, 0xa0, 0xd2, 0x6a, + 0x00, 0x65, 0x4e, 0x58, + 0x00, 0x65, 0xbc, 0x40, + 0x20, 0xa0, 0x5c, 0x6a, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xf4, 0x5b, - 0xff, 0x6a, 0x0a, 0x5c, + 0x00, 0x6a, 0x74, 0x5b, + 0xff, 0x6a, 0x8a, 0x5b, 0xff, 0xf8, 0xc8, 0x08, 0xff, 0x4f, 0xc8, 0x08, - 0x01, 0x6a, 0xf4, 0x5b, - 0x00, 0xb9, 0x0a, 0x5c, + 0x01, 0x6a, 0x74, 0x5b, + 0x00, 0xb9, 0x8a, 0x5b, 0x01, 0x4f, 0x9e, 0x18, 0x02, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xb8, 0x5c, - 0x00, 0x65, 0x34, 0x41, + 0x00, 0x65, 0x38, 0x5c, + 0x00, 0x65, 0xbc, 0x40, 0x41, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0x04, 0xa0, 0x40, 0x01, - 0x00, 0x65, 0xd0, 0x5c, - 0x00, 0x65, 0x34, 0x41, - 0x10, 0x36, 0xaa, 0x7a, + 0x00, 0x65, 0x50, 0x5c, + 0x00, 0x65, 0xbc, 0x40, + 0x10, 0x36, 0x34, 0x7a, 0x05, 0x38, 0x46, 0x31, 0x04, 0x14, 0x58, 0x31, 0x03, 0xa9, 0x60, 0x31, 0xa3, 0x6a, 0xcc, 0x00, - 0x38, 0x6a, 0x30, 0x5c, + 0x38, 0x6a, 0xb0, 0x5b, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0x32, 0x5c, - 0xa9, 0x6a, 0x34, 0x5c, - 0x00, 0x65, 0xaa, 0x42, + 0x14, 0x6a, 0xb2, 0x5b, + 0xa9, 0x6a, 0xb4, 0x5b, + 0x00, 0x65, 0x34, 0x42, 0xef, 0x36, 0x6c, 0x08, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, 0x0f, 0x64, 0xc8, 0x08, 0x07, 0x64, 0xc8, 0x08, 0x00, 0x37, 0x6e, 0x00, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0xc4, 0x5b, - 0xff, 0x51, 0x16, 0x73, - 0x20, 0x36, 0x20, 0x7b, - 0x00, 0x90, 0xb2, 0x5b, - 0x00, 0x65, 0x22, 0x43, + 0x00, 0x65, 0x44, 0x5b, + 0xff, 0x51, 0xa0, 0x72, + 0x20, 0x36, 0xaa, 0x7a, + 0x00, 0x90, 0x32, 0x5b, + 0x00, 0x65, 0xac, 0x42, 0xff, 0x06, 0xd4, 0x08, - 0x00, 0x65, 0x1e, 0x5c, - 0xe0, 0x3d, 0x3c, 0x63, - 0x20, 0x12, 0x3c, 0x63, - 0x51, 0x6a, 0x5c, 0x5b, - 0x00, 0x65, 0xac, 0x5b, + 0x00, 0x65, 0x9e, 0x5b, + 0xe0, 0x3d, 0xc6, 0x62, + 0x20, 0x12, 0xc6, 0x62, + 0x51, 0x6a, 0xda, 0x5a, + 0x00, 0x65, 0x2c, 0x5b, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0xa1, 0x34, 0x63, - 0x04, 0xa0, 0x34, 0x7b, + 0x00, 0xa1, 0xbe, 0x62, + 0x04, 0xa0, 0xbe, 0x7a, 0xfb, 0xa0, 0x40, 0x09, 0x80, 0x36, 0x6c, 0x00, - 0x80, 0xa0, 0xaa, 0x7a, + 0x80, 0xa0, 0x34, 0x7a, 0x7f, 0xa0, 0x40, 0x09, - 0xff, 0x6a, 0x58, 0x5b, - 0x00, 0x65, 0xaa, 0x42, - 0x04, 0xa0, 0x3a, 0x7b, - 0x00, 0x65, 0xd0, 0x5c, - 0x00, 0x65, 0x3c, 0x43, - 0x00, 0x65, 0xb8, 0x5c, + 0xff, 0x6a, 0xd6, 0x5a, + 0x00, 0x65, 0x34, 0x42, + 0x04, 0xa0, 0xc4, 0x7a, + 0x00, 0x65, 0x50, 0x5c, + 0x00, 0x65, 0xc6, 0x42, + 0x00, 0x65, 0x38, 0x5c, 0x31, 0x6a, 0x22, 0x01, - 0x0c, 0x6a, 0x58, 0x5b, - 0x00, 0x65, 0xaa, 0x42, + 0x0c, 0x6a, 0xd6, 0x5a, + 0x00, 0x65, 0x34, 0x42, 0x61, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xaa, 0x42, - 0x51, 0x6a, 0x5c, 0x5b, - 0x20, 0x0d, 0xaa, 0x6a, - 0xff, 0xa8, 0x54, 0x6b, - 0xff, 0xa9, 0x54, 0x6b, - 0xff, 0xaa, 0x54, 0x6b, - 0xff, 0xab, 0x54, 0x6b, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, + 0x51, 0x6a, 0xda, 0x5a, 0x51, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0xaa, 0x42, + 0x00, 0x65, 0x34, 0x42, 0x10, 0x3d, 0x06, 0x00, 0xff, 0x65, 0x68, 0x0c, 0xff, 0x06, 0xd4, 0x08, - 0x01, 0x0c, 0x5e, 0x7b, - 0x04, 0x0c, 0x60, 0x6b, + 0x01, 0x0c, 0xdc, 0x7a, + 0x04, 0x0c, 0xde, 0x6a, 0xe0, 0x03, 0x7a, 0x08, - 0xe0, 0x3d, 0x72, 0x63, + 0xe0, 0x3d, 0xea, 0x62, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x12, 0xda, 0x0c, 0xff, 0x06, 0xd4, 0x0c, - 0xff, 0x65, 0x0c, 0x08, - 0x02, 0x0b, 0x6e, 0x7b, - 0xff, 0x6a, 0xd4, 0x0c, 0xd1, 0x6a, 0x22, 0x01, - 0x00, 0x65, 0x22, 0x41, + 0x00, 0x65, 0xaa, 0x40, 0xff, 0x65, 0x26, 0x09, - 0x01, 0x0b, 0x86, 0x6b, - 0x10, 0x0c, 0x78, 0x7b, - 0x04, 0x0b, 0x80, 0x6b, + 0x01, 0x0b, 0xfe, 0x6a, + 0x10, 0x0c, 0xf0, 0x7a, + 0x04, 0x0b, 0xf8, 0x6a, 0xff, 0x6a, 0xca, 0x08, - 0x04, 0x93, 0x84, 0x6b, - 0x01, 0x94, 0x82, 0x7b, - 0x10, 0x94, 0x84, 0x6b, + 0x04, 0x93, 0xfc, 0x6a, + 0x01, 0x94, 0xfa, 0x7a, + 0x10, 0x94, 0xfc, 0x6a, + 0x80, 0x3d, 0x02, 0x73, + 0x0f, 0x04, 0x06, 0x6b, + 0x02, 0x03, 0x06, 0x7b, + 0x11, 0x0c, 0x02, 0x7b, 0xc7, 0x93, 0x26, 0x09, 0xff, 0x99, 0xd4, 0x08, - 0x38, 0x93, 0x88, 0x6b, + 0x38, 0x93, 0x08, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0x36, 0x8c, 0x6b, + 0x80, 0x36, 0x0c, 0x6b, 0x21, 0x6a, 0x22, 0x05, 0xff, 0x65, 0x20, 0x09, - 0xff, 0x51, 0x9a, 0x63, + 0xff, 0x51, 0x1a, 0x63, 0xff, 0x37, 0xc8, 0x08, - 0xa1, 0x6a, 0xa6, 0x43, + 0xa1, 0x6a, 0x26, 0x43, 0xff, 0x51, 0xc8, 0x08, - 0xb9, 0x6a, 0xa6, 0x43, + 0xb9, 0x6a, 0x26, 0x43, 0xff, 0x90, 0xa4, 0x08, - 0xff, 0xba, 0xaa, 0x73, + 0xff, 0xba, 0x2a, 0x73, 0xff, 0xba, 0x20, 0x09, 0xff, 0x65, 0xca, 0x18, - 0x00, 0x6c, 0x9e, 0x63, + 0x00, 0x6c, 0x1e, 0x63, 0xff, 0x90, 0xca, 0x0c, 0xff, 0x6a, 0xca, 0x04, - 0x20, 0x36, 0xbe, 0x7b, - 0x00, 0x90, 0x92, 0x5b, - 0xff, 0x65, 0xbe, 0x73, - 0xff, 0x52, 0xbc, 0x73, + 0x20, 0x36, 0x3e, 0x7b, + 0x00, 0x90, 0x12, 0x5b, + 0xff, 0x65, 0x3e, 0x73, + 0xff, 0x52, 0x3c, 0x73, 0xff, 0xba, 0xcc, 0x08, 0xff, 0x52, 0x20, 0x09, 0xff, 0x66, 0x74, 0x09, 0xff, 0x65, 0x20, 0x0d, 0xff, 0xba, 0x7e, 0x0c, - 0x00, 0x6a, 0xbe, 0x5c, + 0x00, 0x6a, 0x3e, 0x5c, 0x0d, 0x6a, 0x6a, 0x00, - 0x00, 0x51, 0x4a, 0x44, - 0xff, 0x3f, 0x18, 0x74, + 0x00, 0x51, 0xca, 0x43, + 0xff, 0x3f, 0x98, 0x73, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3f, 0x92, 0x5b, - 0xff, 0x65, 0x18, 0x74, + 0x00, 0x3f, 0x12, 0x5b, + 0xff, 0x65, 0x98, 0x73, 0x20, 0x36, 0x6c, 0x00, - 0x20, 0xa0, 0xd2, 0x6b, + 0x20, 0xa0, 0x52, 0x6b, 0xff, 0xb9, 0xa2, 0x0c, 0xff, 0x6a, 0xa2, 0x04, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xd6, 0x01, - 0x80, 0xeb, 0xde, 0x7b, + 0x80, 0xeb, 0x5e, 0x7b, 0x01, 0x6a, 0xd6, 0x01, 0x01, 0xe9, 0xa4, 0x34, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x0d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, + 0x00, 0x65, 0x30, 0x5c, 0xff, 0x99, 0xa4, 0x0c, 0xff, 0x65, 0xa4, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x45, 0x6a, 0x3e, 0x5c, + 0x45, 0x6a, 0xbe, 0x5b, 0x01, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x0e, 0x7c, + 0x80, 0xee, 0x8e, 0x7b, 0xff, 0x6a, 0xdc, 0x0d, 0xff, 0x65, 0x32, 0x09, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x44, + 0x00, 0x65, 0x30, 0x44, 0xff, 0x37, 0xc8, 0x08, - 0x00, 0x6a, 0xd4, 0x5b, + 0x00, 0x6a, 0x54, 0x5b, 0xff, 0x52, 0xa2, 0x0c, - 0x01, 0x0c, 0x1e, 0x7c, - 0x04, 0x0c, 0x1e, 0x6c, + 0x01, 0x0c, 0x9e, 0x7b, + 0x04, 0x0c, 0x9e, 0x6b, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7a, 0x0c, 0xff, 0x8c, 0x10, 0x08, @@ -553,29 +489,29 @@ static unsigned char seqprog[] = { 0x00, 0x6c, 0xda, 0x24, 0xff, 0x65, 0xc8, 0x08, 0xe0, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x3a, 0x5c, + 0x41, 0x6a, 0xba, 0x5b, 0xff, 0x90, 0xe2, 0x09, 0x20, 0x6a, 0xd0, 0x01, - 0x04, 0x35, 0x5c, 0x7c, + 0x04, 0x35, 0xdc, 0x7b, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0x58, 0x64, - 0x00, 0x65, 0x68, 0x44, + 0xdc, 0xee, 0xd8, 0x63, + 0x00, 0x65, 0xe8, 0x43, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x62, 0x7c, + 0x80, 0xee, 0xe2, 0x7b, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0x66, 0x64, + 0xd8, 0xee, 0xe6, 0x63, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0x6a, 0x6c, + 0x18, 0xee, 0xea, 0x6b, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x41, 0x6a, 0x3a, 0x5c, + 0x41, 0x6a, 0xba, 0x5b, 0x20, 0x6a, 0x18, 0x01, 0xff, 0x6a, 0x1a, 0x09, 0xff, 0x6a, 0x1c, 0x09, 0xff, 0x35, 0x26, 0x09, - 0x04, 0x35, 0x94, 0x6c, + 0x04, 0x35, 0x14, 0x6c, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, 0xff, 0x6c, 0x32, 0x09, @@ -586,14 +522,14 @@ static unsigned char seqprog[] = { 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, 0xff, 0x6c, 0x32, 0x09, - 0x00, 0x65, 0x80, 0x64, + 0x00, 0x65, 0x00, 0x64, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xb0, 0x5c, - 0x04, 0x35, 0x8c, 0x7b, - 0xa0, 0x6a, 0xa0, 0x5c, - 0x00, 0x65, 0xa2, 0x5c, - 0x00, 0x65, 0xa2, 0x5c, - 0x00, 0x65, 0xa2, 0x44, + 0x00, 0x65, 0x30, 0x5c, + 0x04, 0x35, 0x0c, 0x7b, + 0xa0, 0x6a, 0x20, 0x5c, + 0x00, 0x65, 0x22, 0x5c, + 0x00, 0x65, 0x22, 0x5c, + 0x00, 0x65, 0x22, 0x44, 0xff, 0x65, 0xcc, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, @@ -602,19 +538,19 @@ static unsigned char seqprog[] = { 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x08, 0xff, 0x99, 0xda, 0x0c, - 0x08, 0x94, 0xb0, 0x7c, + 0x08, 0x94, 0x30, 0x7c, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0xb4, 0x6c, + 0x08, 0x93, 0x34, 0x6c, 0xff, 0x6a, 0xd4, 0x0c, 0xff, 0x40, 0x74, 0x09, 0xff, 0x90, 0x80, 0x08, 0xff, 0x6a, 0x72, 0x05, - 0xff, 0x40, 0xcc, 0x64, - 0xff, 0x3f, 0xc4, 0x64, + 0xff, 0x40, 0x4c, 0x64, + 0xff, 0x3f, 0x44, 0x64, 0xff, 0x6a, 0xca, 0x04, 0xff, 0x3f, 0x20, 0x09, 0x01, 0x6a, 0x6a, 0x00, - 0x00, 0xb9, 0x4a, 0x5c, + 0x00, 0xb9, 0xca, 0x5b, 0xff, 0xba, 0x7e, 0x0c, 0xff, 0x40, 0x20, 0x09, 0xff, 0xba, 0x80, 0x0c, @@ -622,20 +558,12 @@ static unsigned char seqprog[] = { 0xff, 0x90, 0x7e, 0x0c, }; -static int aic7xxx_patch13_func(struct aic7xxx_host *p); - -static int -aic7xxx_patch13_func(struct aic7xxx_host *p) -{ - return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); -} - static int aic7xxx_patch12_func(struct aic7xxx_host *p); static int aic7xxx_patch12_func(struct aic7xxx_host *p) { - return ((p->features & AHC_CMD_CHAN) == 0); + return ((p->features & AHC_WIDE) != 0); } static int aic7xxx_patch11_func(struct aic7xxx_host *p); @@ -643,7 +571,7 @@ static int aic7xxx_patch11_func(struct aic7xxx_host *p); static int aic7xxx_patch11_func(struct aic7xxx_host *p) { - return ((p->features & AHC_WIDE) != 0); + return ((p->features & AHC_ULTRA2) == 0); } static int aic7xxx_patch10_func(struct aic7xxx_host *p); @@ -651,7 +579,7 @@ static int aic7xxx_patch10_func(struct aic7xxx_host *p); static int aic7xxx_patch10_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA2) == 0); + return ((p->features & AHC_CMD_CHAN) == 0); } static int aic7xxx_patch9_func(struct aic7xxx_host *p); @@ -659,7 +587,7 @@ static int aic7xxx_patch9_func(struct aic7xxx_host *p); static int aic7xxx_patch9_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA) != 0); + return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895); } static int aic7xxx_patch8_func(struct aic7xxx_host *p); @@ -667,7 +595,7 @@ static int aic7xxx_patch8_func(struct aic7xxx_host *p); static int aic7xxx_patch8_func(struct aic7xxx_host *p) { - return ((p->features & AHC_ULTRA2) != 0); + return ((p->features & AHC_ULTRA) != 0); } static int aic7xxx_patch7_func(struct aic7xxx_host *p); @@ -675,7 +603,7 @@ static int aic7xxx_patch7_func(struct aic7xxx_host *p); static int aic7xxx_patch7_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_PAGESCBS) == 0); + return ((p->features & AHC_ULTRA2) != 0); } static int aic7xxx_patch6_func(struct aic7xxx_host *p); @@ -683,7 +611,7 @@ static int aic7xxx_patch6_func(struct aic7xxx_host *p); static int aic7xxx_patch6_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_PAGESCBS) != 0); + return ((p->flags & AHC_PAGESCBS) == 0); } static int aic7xxx_patch5_func(struct aic7xxx_host *p); @@ -691,7 +619,7 @@ static int aic7xxx_patch5_func(struct aic7xxx_host *p); static int aic7xxx_patch5_func(struct aic7xxx_host *p) { - return ((p->features & AHC_QUEUE_REGS) != 0); + return ((p->flags & AHC_PAGESCBS) != 0); } static int aic7xxx_patch4_func(struct aic7xxx_host *p); @@ -699,7 +627,7 @@ static int aic7xxx_patch4_func(struct aic7xxx_host *p); static int aic7xxx_patch4_func(struct aic7xxx_host *p) { - return ((p->features & AHC_TWIN) != 0); + return ((p->features & AHC_QUEUE_REGS) != 0); } static int aic7xxx_patch3_func(struct aic7xxx_host *p); @@ -707,7 +635,7 @@ static int aic7xxx_patch3_func(struct aic7xxx_host *p); static int aic7xxx_patch3_func(struct aic7xxx_host *p) { - return ((p->features & AHC_QUEUE_REGS) == 0); + return ((p->features & AHC_TWIN) != 0); } static int aic7xxx_patch2_func(struct aic7xxx_host *p); @@ -715,7 +643,7 @@ static int aic7xxx_patch2_func(struct aic7xxx_host *p); static int aic7xxx_patch2_func(struct aic7xxx_host *p) { - return ((p->features & AHC_CMD_CHAN) != 0); + return ((p->features & AHC_QUEUE_REGS) == 0); } static int aic7xxx_patch1_func(struct aic7xxx_host *p); @@ -723,7 +651,7 @@ static int aic7xxx_patch1_func(struct aic7xxx_host *p); static int aic7xxx_patch1_func(struct aic7xxx_host *p) { - return ((p->flags & AHC_TARGETMODE) != 0); + return ((p->features & AHC_CMD_CHAN) != 0); } static int aic7xxx_patch0_func(struct aic7xxx_host *p); @@ -740,84 +668,78 @@ struct sequencer_patch { skip_instr :10, skip_patch :12; } sequencer_patches[] = { - { aic7xxx_patch1_func, 2, 1, 2 }, - { aic7xxx_patch0_func, 3, 1, 1 }, - { aic7xxx_patch2_func, 4, 2, 1 }, - { aic7xxx_patch3_func, 8, 1, 1 }, - { aic7xxx_patch3_func, 9, 1, 1 }, - { aic7xxx_patch4_func, 12, 4, 1 }, - { aic7xxx_patch5_func, 17, 3, 2 }, - { aic7xxx_patch0_func, 20, 4, 1 }, - { aic7xxx_patch6_func, 24, 1, 1 }, - { aic7xxx_patch7_func, 27, 1, 1 }, - { aic7xxx_patch2_func, 30, 1, 2 }, - { aic7xxx_patch0_func, 31, 3, 1 }, - { aic7xxx_patch4_func, 40, 4, 1 }, - { aic7xxx_patch8_func, 44, 3, 2 }, - { aic7xxx_patch0_func, 47, 3, 1 }, - { aic7xxx_patch9_func, 52, 7, 1 }, - { aic7xxx_patch4_func, 60, 3, 1 }, - { aic7xxx_patch8_func, 63, 2, 1 }, - { aic7xxx_patch1_func, 68, 60, 1 }, - { aic7xxx_patch8_func, 162, 1, 2 }, - { aic7xxx_patch0_func, 163, 2, 1 }, - { aic7xxx_patch2_func, 167, 3, 3 }, - { aic7xxx_patch8_func, 167, 2, 1 }, - { aic7xxx_patch0_func, 170, 2, 1 }, - { aic7xxx_patch8_func, 173, 1, 2 }, - { aic7xxx_patch0_func, 174, 1, 1 }, - { aic7xxx_patch2_func, 178, 1, 1 }, - { aic7xxx_patch2_func, 181, 3, 2 }, - { aic7xxx_patch0_func, 184, 5, 1 }, - { aic7xxx_patch2_func, 192, 2, 3 }, - { aic7xxx_patch8_func, 192, 1, 1 }, - { aic7xxx_patch0_func, 194, 3, 1 }, - { aic7xxx_patch10_func, 198, 1, 2 }, - { aic7xxx_patch0_func, 199, 1, 1 }, - { aic7xxx_patch8_func, 200, 7, 2 }, - { aic7xxx_patch0_func, 207, 1, 1 }, - { aic7xxx_patch2_func, 212, 14, 3 }, - { aic7xxx_patch10_func, 225, 1, 1 }, - { aic7xxx_patch0_func, 226, 9, 1 }, - { aic7xxx_patch8_func, 240, 2, 1 }, - { aic7xxx_patch8_func, 242, 1, 1 }, - { aic7xxx_patch10_func, 243, 6, 3 }, - { aic7xxx_patch2_func, 243, 2, 2 }, - { aic7xxx_patch0_func, 245, 4, 1 }, - { aic7xxx_patch8_func, 250, 1, 1 }, - { aic7xxx_patch8_func, 254, 19, 1 }, - { aic7xxx_patch2_func, 274, 3, 3 }, - { aic7xxx_patch10_func, 276, 1, 1 }, - { aic7xxx_patch0_func, 277, 5, 1 }, - { aic7xxx_patch10_func, 282, 1, 2 }, - { aic7xxx_patch0_func, 283, 9, 1 }, - { aic7xxx_patch11_func, 299, 1, 2 }, - { aic7xxx_patch0_func, 300, 1, 1 }, - { aic7xxx_patch5_func, 361, 1, 2 }, - { aic7xxx_patch0_func, 362, 1, 1 }, - { aic7xxx_patch3_func, 365, 1, 1 }, - { aic7xxx_patch2_func, 375, 3, 2 }, - { aic7xxx_patch0_func, 378, 5, 1 }, - { aic7xxx_patch11_func, 386, 1, 2 }, - { aic7xxx_patch0_func, 387, 1, 1 }, - { aic7xxx_patch6_func, 392, 1, 1 }, - { aic7xxx_patch8_func, 420, 1, 2 }, - { aic7xxx_patch0_func, 421, 5, 1 }, - { aic7xxx_patch1_func, 438, 3, 1 }, - { aic7xxx_patch10_func, 443, 11, 1 }, - { aic7xxx_patch2_func, 491, 7, 2 }, - { aic7xxx_patch0_func, 498, 8, 1 }, - { aic7xxx_patch2_func, 507, 4, 2 }, - { aic7xxx_patch0_func, 511, 6, 1 }, - { aic7xxx_patch2_func, 517, 4, 2 }, - { aic7xxx_patch0_func, 521, 3, 1 }, - { aic7xxx_patch12_func, 531, 10, 1 }, - { aic7xxx_patch2_func, 550, 17, 4 }, - { aic7xxx_patch13_func, 558, 4, 2 }, - { aic7xxx_patch0_func, 562, 2, 1 }, - { aic7xxx_patch0_func, 567, 33, 1 }, - { aic7xxx_patch12_func, 600, 4, 1 }, - { aic7xxx_patch6_func, 604, 2, 1 }, - { aic7xxx_patch6_func, 607, 9, 1 }, + { aic7xxx_patch1_func, 3, 2, 1 }, + { aic7xxx_patch2_func, 7, 1, 1 }, + { aic7xxx_patch2_func, 8, 1, 1 }, + { aic7xxx_patch3_func, 11, 4, 1 }, + { aic7xxx_patch4_func, 16, 3, 2 }, + { aic7xxx_patch0_func, 19, 4, 1 }, + { aic7xxx_patch5_func, 23, 1, 1 }, + { aic7xxx_patch6_func, 26, 1, 1 }, + { aic7xxx_patch1_func, 29, 1, 2 }, + { aic7xxx_patch0_func, 30, 3, 1 }, + { aic7xxx_patch3_func, 39, 4, 1 }, + { aic7xxx_patch7_func, 43, 3, 2 }, + { aic7xxx_patch0_func, 46, 3, 1 }, + { aic7xxx_patch8_func, 52, 7, 1 }, + { aic7xxx_patch3_func, 60, 3, 1 }, + { aic7xxx_patch7_func, 63, 2, 1 }, + { aic7xxx_patch7_func, 102, 1, 2 }, + { aic7xxx_patch0_func, 103, 2, 1 }, + { aic7xxx_patch7_func, 107, 2, 1 }, + { aic7xxx_patch9_func, 109, 1, 1 }, + { aic7xxx_patch10_func, 110, 2, 1 }, + { aic7xxx_patch7_func, 113, 1, 2 }, + { aic7xxx_patch0_func, 114, 1, 1 }, + { aic7xxx_patch1_func, 118, 1, 1 }, + { aic7xxx_patch1_func, 121, 3, 2 }, + { aic7xxx_patch0_func, 124, 5, 1 }, + { aic7xxx_patch1_func, 132, 2, 3 }, + { aic7xxx_patch7_func, 132, 1, 1 }, + { aic7xxx_patch0_func, 134, 3, 1 }, + { aic7xxx_patch11_func, 138, 1, 2 }, + { aic7xxx_patch0_func, 139, 1, 1 }, + { aic7xxx_patch7_func, 140, 7, 2 }, + { aic7xxx_patch0_func, 147, 1, 1 }, + { aic7xxx_patch1_func, 152, 14, 3 }, + { aic7xxx_patch11_func, 165, 1, 1 }, + { aic7xxx_patch0_func, 166, 9, 1 }, + { aic7xxx_patch7_func, 180, 2, 1 }, + { aic7xxx_patch7_func, 182, 1, 1 }, + { aic7xxx_patch11_func, 183, 6, 3 }, + { aic7xxx_patch1_func, 183, 2, 2 }, + { aic7xxx_patch0_func, 185, 4, 1 }, + { aic7xxx_patch7_func, 190, 1, 1 }, + { aic7xxx_patch7_func, 194, 20, 1 }, + { aic7xxx_patch1_func, 215, 3, 3 }, + { aic7xxx_patch11_func, 217, 1, 1 }, + { aic7xxx_patch0_func, 218, 5, 1 }, + { aic7xxx_patch11_func, 223, 1, 2 }, + { aic7xxx_patch0_func, 224, 9, 1 }, + { aic7xxx_patch12_func, 240, 1, 2 }, + { aic7xxx_patch0_func, 241, 1, 1 }, + { aic7xxx_patch4_func, 302, 1, 2 }, + { aic7xxx_patch0_func, 303, 1, 1 }, + { aic7xxx_patch2_func, 306, 1, 1 }, + { aic7xxx_patch1_func, 316, 3, 2 }, + { aic7xxx_patch0_func, 319, 5, 1 }, + { aic7xxx_patch12_func, 327, 1, 2 }, + { aic7xxx_patch0_func, 328, 1, 1 }, + { aic7xxx_patch5_func, 333, 1, 1 }, + { aic7xxx_patch11_func, 375, 15, 1 }, + { aic7xxx_patch1_func, 427, 7, 2 }, + { aic7xxx_patch0_func, 434, 8, 1 }, + { aic7xxx_patch1_func, 443, 4, 2 }, + { aic7xxx_patch0_func, 447, 6, 1 }, + { aic7xxx_patch1_func, 453, 4, 2 }, + { aic7xxx_patch0_func, 457, 3, 1 }, + { aic7xxx_patch10_func, 467, 10, 1 }, + { aic7xxx_patch1_func, 486, 17, 4 }, + { aic7xxx_patch9_func, 494, 4, 2 }, + { aic7xxx_patch0_func, 498, 2, 1 }, + { aic7xxx_patch0_func, 503, 33, 1 }, + { aic7xxx_patch10_func, 536, 4, 1 }, + { aic7xxx_patch5_func, 540, 2, 1 }, + { aic7xxx_patch5_func, 543, 9, 1 }, }; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 6ae59e9dd..b7d3b9cc1 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -176,6 +176,8 @@ extern void scsi_old_times_out(Scsi_Cmnd * SCpnt); */ void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) { blk_init_queue(&SDpnt->request_queue, scsi_request_fn); + blk_queue_headactive(&SDpnt->request_queue, 0); + SDpnt->request_queue.queuedata = (void *) SDpnt; } #ifdef MODULE @@ -2567,7 +2569,6 @@ static void scsi_dump_status(int level) } } } - printk("wait_for_request = %p\n", &wait_for_request); #endif /* CONFIG_SCSI_LOGGING */ /* } */ } #endif /* CONFIG_PROC_FS */ @@ -2728,8 +2729,6 @@ Scsi_Device * scsi_get_host_dev(struct Scsi_Host * SHpnt) scsi_build_commandblocks(SDpnt); scsi_initialize_queue(SDpnt, SHpnt); - blk_queue_headactive(&SDpnt->request_queue, 0); - SDpnt->request_queue.queuedata = (void *) SDpnt; SDpnt->online = TRUE; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index d4a1fd5fb..042493e3e 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -855,10 +855,9 @@ void scsi_request_fn(request_queue_t * q) * if the device itself is blocked, or if the host is fully * occupied. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } + /* * To start with, we keep looping until the queue is empty, or until * the host is no longer able to accept any more requests. @@ -869,10 +868,8 @@ void scsi_request_fn(request_queue_t * q) * released the lock and grabbed it again, so each time * we need to check to see if the queue is plugged or not. */ - if (SHpnt->in_recovery - || q->plugged) { + if (SHpnt->in_recovery || q->plugged) return; - } /* * If the device cannot accept another request, then quit. @@ -1019,8 +1016,7 @@ void scsi_request_fn(request_queue_t * q) * We have copied the data out of the request block - it is now in * a field in SCpnt. Release the request block. */ - req->rq_status = RQ_INACTIVE; - wake_up(&wait_for_request); + blkdev_release_request(req); } /* * Now it is finally safe to release the lock. We are diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 56ef67cb2..152f8afc3 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -271,8 +271,8 @@ void scan_scsis(struct Scsi_Host *shpnt, if (SDpnt) { memset(SDpnt, 0, sizeof(Scsi_Device)); /* - * Register the queue for the device. All I/O requests will come - * in through here. We also need to register a pointer to + * Register the queue for the device. All I/O requests will + * come in through here. We also need to register a pointer to * ourselves, since the queue handler won't know what device * the queue actually represents. We could look it up, but it * is pointless work. diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 71b93e379..4bfdc59f4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1230,9 +1230,9 @@ static int sg_attach(Scsi_Device * scsidp) sdp->detached = 0; sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k); - sdp->de = devfs_register (scsidp->de, "generic", 7, DEVFS_FL_NONE, + sdp->de = devfs_register (scsidp->de, "generic", DEVFS_FL_DEFAULT, SCSI_GENERIC_MAJOR, k, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, &sg_fops, NULL); sg_template.nr_dev++; sg_dev_arr[k] = sdp; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index e568c6d1b..cd3f7a488 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -777,9 +777,9 @@ void sr_finish() sprintf(name, "sr%d", i); strcpy(scsi_CDs[i].cdi.name, name); scsi_CDs[i].cdi.de = - devfs_register (scsi_CDs[i].device->de, "cd", 2, + devfs_register (scsi_CDs[i].device->de, "cd", DEVFS_FL_DEFAULT, MAJOR_NR, i, - S_IFBLK | S_IRUGO | S_IWUGO, 0, 0, + S_IFBLK | S_IRUGO | S_IWUGO, &cdrom_fops, NULL); register_cdrom(&scsi_CDs[i].cdi); } @@ -857,7 +857,6 @@ void cleanup_module(void) } blksize_size[MAJOR_NR] = NULL; hardsect_size[MAJOR_NR] = NULL; - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); blk_size[MAJOR_NR] = NULL; read_ahead[MAJOR_NR] = 0; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index a9d08c165..bdc3e8002 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3509,17 +3509,17 @@ static int st_attach(Scsi_Device * SDp) /* Rewind entry */ sprintf (name, "mt%s", formats[mode]); tpnt->de_r[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5), S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &st_fops, NULL); + &st_fops, NULL); /* No-rewind entry */ sprintf (name, "mt%sn", formats[mode]); tpnt->de_n[mode] = - devfs_register (SDp->de, name, 0, DEVFS_FL_DEFAULT, + devfs_register (SDp->de, name, DEVFS_FL_DEFAULT, MAJOR_NR, i + (mode << 5) + 128, S_IFCHR | S_IRUGO | S_IWUGO, - 0, 0, &st_fops, NULL); + &st_fops, NULL); } devfs_register_tape (tpnt->de_r[0]); tpnt->device = SDp; diff --git a/drivers/sgi/char/shmiq.c b/drivers/sgi/char/shmiq.c index 18009a65a..5df43bf8b 100644 --- a/drivers/sgi/char/shmiq.c +++ b/drivers/sgi/char/shmiq.c @@ -457,11 +457,11 @@ shmiq_init (void) { printk ("SHMIQ setup\n"); devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops); - devfs_register (NULL, "shmiq", 0, DEVFS_FL_DEFAULT, - SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + devfs_register (NULL, "shmiq", DEVFS_FL_DEFAULT, + SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR, &shmiq_fops, NULL); devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT, SHMIQ_MAJOR, 1, - S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &shmiq_fops, NULL); } diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index da0cfe51c..9ec821b91 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -175,9 +175,9 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f sprintf (name_buf, "%s", name); else sprintf (name_buf, "%s%d", name, (r - low) / SOUND_STEP); - s->de = devfs_register (devfs_handle, name_buf, 0, + s->de = devfs_register (devfs_handle, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, s->unit_minor, - S_IFCHR | mode, 0, 0, fops, NULL); + S_IFCHR | mode, fops, NULL); return r; } diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index 5018ad1e7..aa7b82280 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -565,9 +565,9 @@ static void soundcard_register_devfs (int do_register) for (j = 0; j < num || j == 0; j++) { soundcard_make_name (name_buf, dev_list[i].name, j); if (do_register) - devfs_register (NULL, name_buf, 0, DEVFS_FL_NONE, + devfs_register (NULL, name_buf, DEVFS_FL_NONE, SOUND_MAJOR, dev_list[i].minor+ (j* 0x10), - S_IFCHR | dev_list[i].mode, 0, 0, + S_IFCHR | dev_list[i].mode, &oss_sound_fops, NULL); else { devfs_handle_t de; diff --git a/drivers/telephony/ixj.h b/drivers/telephony/ixj.h index d88013c2f..3559cc5c0 100644 --- a/drivers/telephony/ixj.h +++ b/drivers/telephony/ixj.h @@ -879,7 +879,6 @@ typedef struct { typedef struct { struct phone_device p; - struct semaphore mutex; unsigned int board; unsigned int DSPbase; unsigned int XILINXbase; diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 042f742da..5032a9b75 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -70,7 +70,10 @@ comment 'USB HID' dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB fi dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB - dep_tristate ' Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB + dep_tristate ' I-Force joysticks/wheels' CONFIG_INPUT_IFORCE_USB $CONFIG_USB + if [ "$CONFIG_INPUT_IFORCE_USB" != "n" ]; then + define_tristate CONFIG_INPUT_IFORCE $CONFIG_INPUT_IFORCE_USB + fi dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ec8687ae6..7d95c0c98 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -61,7 +61,7 @@ obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o obj-$(CONFIG_USB_HID) += hid.o input.o obj-$(CONFIG_USB_KBD) += usbkbd.o input.o obj-$(CONFIG_USB_WACOM) += wacom.o input.o -obj-$(CONFIG_USB_WMFORCE) += wmforce.o input.o +obj-$(CONFIG_INPUT_IFORCE) += iforce.o input.o obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o input.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o input.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o input.o diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c index 235385515..d6cedee9b 100644 --- a/drivers/usb/devio.c +++ b/drivers/usb/devio.c @@ -1044,7 +1044,7 @@ static int proc_ioctl (struct dev_state *ps, void *arg) kfree (buf); return -EFAULT; } else - memset (arg, size, 0); + memset (arg, 0, size); } /* ioctl to device */ diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c index 3e21f6cb5..7eca5e304 100644 --- a/drivers/usb/evdev.c +++ b/drivers/usb/evdev.c @@ -1,5 +1,5 @@ /* - * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $ + * $Id: evdev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -39,7 +39,7 @@ #include <linux/input.h> struct evdev { - int used; + int exist; int open; int minor; struct input_handle handle; @@ -99,13 +99,14 @@ static int evdev_release(struct inode * inode, struct file * file) listptr = &((*listptr)->next); *listptr = (*listptr)->next; - if (!--list->evdev->open) - input_close_device(&list->evdev->handle); - - if (!--list->evdev->used) { - input_unregister_minor(list->evdev->devfs); - evdev_table[list->evdev->minor] = NULL; - kfree(list->evdev); + if (!--list->evdev->open) { + if (list->evdev->exist) { + input_close_device(&list->evdev->handle); + } else { + input_unregister_minor(list->evdev->devfs); + evdev_table[list->evdev->minor] = NULL; + kfree(list->evdev); + } } kfree(list); @@ -121,9 +122,8 @@ static int evdev_open(struct inode * inode, struct file * file) if (i > EVDEV_MINORS || !evdev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) { + if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) return -ENOMEM; - } memset(list, 0, sizeof(struct evdev_list)); list->evdev = evdev_table[i]; @@ -132,10 +132,9 @@ static int evdev_open(struct inode * inode, struct file * file) file->private_data = list; - list->evdev->used++; - if (!list->evdev->open++) - input_open_device(&list->evdev->handle); + if (list->evdev->exist) + input_open_device(&list->evdev->handle); return 0; } @@ -303,7 +302,7 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct evdev->handle.handler = handler; evdev->handle.private = evdev; - evdev->used = 1; + evdev->exist = 1; evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); @@ -316,10 +315,11 @@ static void evdev_disconnect(struct input_handle *handle) { struct evdev *evdev = handle->private; - if (evdev->open) - input_close_device(handle); + evdev->exist = 0; - if (!--evdev->used) { + if (evdev->open) { + input_close_device(handle); + } else { input_unregister_minor(evdev->devfs); evdev_table[evdev->minor] = NULL; kfree(evdev); diff --git a/drivers/usb/iforce.c b/drivers/usb/iforce.c new file mode 100644 index 000000000..a61b7801f --- /dev/null +++ b/drivers/usb/iforce.c @@ -0,0 +1,335 @@ +/* + * $Id: iforce.c,v 1.7 2000/06/04 14:03:36 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * USB/RS232 I-Force joysticks and wheels. + * + * Sponsored by SuSE + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/serio.h> +#include <linux/config.h> + +MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 + +#define IFORCE_MAX_LENGTH 16 + +#if defined(CONFIG_INPUT_IFORCE_232) || defined(CONFIG_INPUT_IFORCE_232_MODULE) +#define IFORCE_232 +#endif +#if defined(CONFIG_INPUT_IFORCE_USB) || defined(CONFIG_INPUT_IFORCE_USB_MODULE) +#define IFORCE_USB +#endif + +struct iforce { + signed char data[IFORCE_MAX_LENGTH]; + struct input_dev dev; + struct urb irq; + int open; + int idx, pkt, len, id; + unsigned char csum; +}; + +static struct { + __s32 x; + __s32 y; +} iforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *iforce_name = "I-Force joystick/wheel"; + +static void iforce_process_packet(struct input_dev *dev, unsigned char id, int idx, unsigned char *data) +{ + switch (id) { + + case 1: /* joystick position data */ + case 3: /* wheel position data */ + + if (id == 1) { + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[3] << 8) | data[2])); + input_report_abs(dev, ABS_THROTTLE, 255 - data[4]); + } else { + input_report_abs(dev, ABS_WHEEL, (__s16) (((__s16)data[1] << 8) | data[0])); + input_report_abs(dev, ABS_GAS, 255 - data[2]); + input_report_abs(dev, ABS_BRAKE, 255 - data[3]); + } + + input_report_abs(dev, ABS_HAT0X, iforce_hat_to_axis[data[6] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, iforce_hat_to_axis[data[6] >> 4].y); + + input_report_key(dev, BTN_TRIGGER, data[5] & 0x01); + input_report_key(dev, BTN_TOP, data[5] & 0x02); + input_report_key(dev, BTN_THUMB, data[5] & 0x04); + input_report_key(dev, BTN_TOP2, data[5] & 0x08); + input_report_key(dev, BTN_BASE, data[5] & 0x10); + input_report_key(dev, BTN_BASE2, data[5] & 0x20); + input_report_key(dev, BTN_BASE3, data[5] & 0x40); + input_report_key(dev, BTN_BASE4, data[5] & 0x80); + input_report_key(dev, BTN_BASE5, data[6] & 0x01); + input_report_key(dev, BTN_A, data[6] & 0x02); + input_report_key(dev, BTN_B, data[6] & 0x04); + input_report_key(dev, BTN_C, data[6] & 0x08); + break; + + case 2: /* force feedback effect status */ + break; + } +} + + +static int iforce_open(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + + if (dev->idbus == BUS_USB && !iforce->open++) + if (usb_submit_urb(&iforce->irq)) + return -EIO; + + return 0; +} + +static void iforce_close(struct input_dev *dev) +{ + struct iforce *iforce = dev->private; + + if (dev->idbus == BUS_USB && !--iforce->open) + usb_unlink_urb(&iforce->irq); +} + +static void iforce_input_setup(struct iforce *iforce) +{ + int i; + + iforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + iforce->dev.keybit[LONG(BTN_JOYSTICK)] |= BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | + BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); + iforce->dev.keybit[LONG(BTN_GAMEPAD)] |= BIT(BTN_A) | BIT(BTN_B) | BIT(BTN_C); + iforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y) + | BIT(ABS_WHEEL) | BIT(ABS_GAS) | BIT(ABS_BRAKE); + + for (i = ABS_X; i <= ABS_Y; i++) { + iforce->dev.absmax[i] = 1920; + iforce->dev.absmin[i] = -1920; + iforce->dev.absflat[i] = 128; + iforce->dev.absfuzz[i] = 16; + } + + for (i = ABS_THROTTLE; i <= ABS_RUDDER; i++) { + iforce->dev.absmax[i] = 255; + iforce->dev.absmin[i] = 0; + } + + for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { + iforce->dev.absmax[i] = 1; + iforce->dev.absmin[i] = -1; + } + + iforce->dev.private = iforce; + iforce->dev.open = iforce_open; + iforce->dev.close = iforce_close; + + input_register_device(&iforce->dev); +} + +#ifdef IFORCE_USB + +static void iforce_usb_irq(struct urb *urb) +{ + struct iforce *iforce = urb->context; + if (urb->status) return; + iforce_process_packet(&iforce->dev, iforce->data[0], 8, iforce->data + 1); +} + +static void *iforce_usb_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct iforce *iforce; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || + dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) + return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return NULL; + memset(iforce, 0, sizeof(struct iforce)); + + iforce->dev.name = iforce_name; + iforce->dev.idbus = BUS_USB; + iforce->dev.idvendor = dev->descriptor.idVendor; + iforce->dev.idproduct = dev->descriptor.idProduct; + iforce->dev.idversion = dev->descriptor.bcdDevice; + + FILL_INT_URB(&iforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + iforce->data, 8, iforce_usb_irq, iforce, endpoint->bInterval); + + iforce_input_setup(iforce); + + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + iforce->dev.number, iforce_name, dev->bus->busnum, dev->devnum, ifnum); + + return iforce; +} + +static void iforce_usb_disconnect(struct usb_device *dev, void *ptr) +{ + struct iforce *iforce = ptr; + usb_unlink_urb(&iforce->irq); + input_unregister_device(&iforce->dev); + kfree(iforce); +} + +static struct usb_driver iforce_usb_driver = { + name: "iforce", + probe: iforce_usb_probe, + disconnect: iforce_usb_disconnect, +}; + +#endif + +#ifdef IFORCE_232 + +static void iforce_serio_irq(struct serio *serio, unsigned char data, unsigned int flags) +{ + struct iforce* iforce = serio->private; + + if (!iforce->pkt) { + if (data != 0x2b) { + return; + } + iforce->pkt = 1; + return; + } + + if (!iforce->id) { + if (data > 3) { + iforce->pkt = 0; + return; + } + iforce->id = data; + return; + } + + if (!iforce->len) { + if (data > IFORCE_MAX_LENGTH) { + iforce->pkt = 0; + iforce->id = 0; + return; + } + iforce->len = data; + return; + } + + if (iforce->idx < iforce->len) { + iforce->csum += iforce->data[iforce->idx++] = data; + return; + } + + if (iforce->idx == iforce->len) { + iforce_process_packet(&iforce->dev, iforce->id, iforce->idx, iforce->data); + iforce->pkt = 0; + iforce->id = 0; + iforce->len = 0; + iforce->idx = 0; + iforce->csum = 0; + } +} + +static void iforce_serio_connect(struct serio *serio, struct serio_dev *dev) +{ + struct iforce *iforce; + + if (serio->type != (SERIO_RS232 | SERIO_IFORCE)) + return; + + if (!(iforce = kmalloc(sizeof(struct iforce), GFP_KERNEL))) return; + memset(iforce, 0, sizeof(struct iforce)); + + iforce->dev.name = iforce_name; + iforce->dev.idbus = BUS_RS232; + iforce->dev.idvendor = SERIO_IFORCE; + iforce->dev.idproduct = 0x0001; + iforce->dev.idversion = 0x0100; + + serio->private = iforce; + + if (serio_open(serio, dev)) { + kfree(iforce); + return; + } + + iforce_input_setup(iforce); + + printk(KERN_INFO "input%d: %s on serio%d\n", + iforce->dev.number, iforce_name, serio->number); +} + +static void iforce_serio_disconnect(struct serio *serio) +{ + struct iforce* iforce = serio->private; + input_unregister_device(&iforce->dev); + serio_close(serio); + kfree(iforce); +} + +static struct serio_dev iforce_serio_dev = { + interrupt: iforce_serio_irq, + connect: iforce_serio_connect, + disconnect: iforce_serio_disconnect, +}; + +#endif + +static int __init iforce_init(void) +{ +#ifdef IFORCE_USB + usb_register(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_register_device(&iforce_serio_dev); +#endif + return 0; +} + +static void __exit iforce_exit(void) +{ +#ifdef IFORCE_USB + usb_deregister(&iforce_usb_driver); +#endif +#ifdef IFORCE_232 + serio_unregister_device(&iforce_serio_dev); +#endif +} + +module_init(iforce_init); +module_exit(iforce_exit); diff --git a/drivers/usb/input.c b/drivers/usb/input.c index 66a3d2911..4fb2985af 100644 --- a/drivers/usb/input.c +++ b/drivers/usb/input.c @@ -386,8 +386,8 @@ devfs_handle_t input_register_minor(char *name, int minor, int minor_base) { char devfs_name[16]; sprintf(devfs_name, name, minor); - return devfs_register(input_devfs_handle, devfs_name, 0, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, - S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, &input_fops, NULL); + return devfs_register(input_devfs_handle, devfs_name, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, + S_IFCHR | S_IRUGO | S_IWUSR, &input_fops, NULL); } void input_unregister_minor(devfs_handle_t handle) diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c index 0f0bb2773..5349d515c 100644 --- a/drivers/usb/joydev.c +++ b/drivers/usb/joydev.c @@ -1,7 +1,7 @@ /* - * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $ + * $Id: joydev.c,v 1.11 2000/06/23 09:23:00 vojtech Exp $ * - * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999-2000 Vojtech Pavlik * Copyright (c) 1999 Colin Van Dyke * * Joystick device driver for the input driver suite. @@ -50,7 +50,7 @@ #define JOYDEV_BUFFER_SIZE 64 struct joydev { - int used; + int exist; int open; int minor; struct input_handle handle; @@ -66,6 +66,7 @@ struct joydev { __u16 keypam[KEY_MAX - BTN_MISC]; __u8 absmap[ABS_MAX]; __u8 abspam[ABS_MAX]; + __s16 abs[ABS_MAX]; }; struct joydev_list { @@ -121,7 +122,9 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne case EV_ABS: event.type = JS_EVENT_AXIS; event.number = joydev->absmap[code]; - event.value = joydev_correct(value, &joydev->corr[event.number]); + event.value = joydev_correct(value, joydev->corr + event.number); + if (event.value == joydev->abs[event.number]) return; + joydev->abs[event.number] = event.value; break; default: @@ -165,13 +168,14 @@ static int joydev_release(struct inode * inode, struct file * file) listptr = &((*listptr)->next); *listptr = (*listptr)->next; - if (!--list->joydev->open) - input_close_device(&list->joydev->handle); - - if (!--list->joydev->used) { - input_unregister_minor(list->joydev->devfs); - joydev_table[list->joydev->minor] = NULL; - kfree(list->joydev); + if (!--list->joydev->open) { + if (list->joydev->exist) { + input_close_device(&list->joydev->handle); + } else { + input_unregister_minor(list->joydev->devfs); + joydev_table[list->joydev->minor] = NULL; + kfree(list->joydev); + } } kfree(list); @@ -187,9 +191,8 @@ static int joydev_open(struct inode *inode, struct file *file) if (i > JOYDEV_MINORS || !joydev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) { + if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) return -ENOMEM; - } memset(list, 0, sizeof(struct joydev_list)); list->joydev = joydev_table[i]; @@ -198,10 +201,9 @@ static int joydev_open(struct inode *inode, struct file *file) file->private_data = list; - list->joydev->used++; - if (!list->joydev->open++) - input_open_device(&list->joydev->handle); + if (list->joydev->exist) + input_open_device(&list->joydev->handle); return 0; } @@ -228,8 +230,8 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); - data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; - data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y; + data.x = (joydev->abs[0] / 256 + 128) >> joydev->glue.JS_CORR.x; + data.y = (joydev->abs[1] / 256 + 128) >> joydev->glue.JS_CORR.y; if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) return -EFAULT; @@ -274,13 +276,12 @@ static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *p if (list->startup < joydev->nkey) { event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; - event.value = !!test_bit(joydev->keypam[list->startup], input->key); event.number = list->startup; + event.value = !!test_bit(joydev->keypam[event.number], input->key); } else { event.type = JS_EVENT_AXIS | JS_EVENT_INIT; - event.value = joydev_correct(input->abs[joydev->abspam[list->startup - joydev->nkey]], - &joydev->corr[list->startup - joydev->nkey]); event.number = list->startup - joydev->nkey; + event.value = joydev->abs[event.number]; } if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) @@ -407,7 +408,7 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev->handle.handler = handler; joydev->handle.private = joydev; - joydev->used = 1; + joydev->exist = 1; for (i = 0; i < ABS_MAX; i++) if (test_bit(i, dev->absbit)) { @@ -442,6 +443,8 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + + joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i); } joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); @@ -455,10 +458,11 @@ static void joydev_disconnect(struct input_handle *handle) { struct joydev *joydev = handle->private; - if (joydev->open) - input_close_device(handle); + joydev->exist = 0; - if (!--joydev->used) { + if (joydev->open) { + input_close_device(handle); + } else { input_unregister_minor(joydev->devfs); joydev_table[joydev->minor] = NULL; kfree(joydev); diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c index 399d2a0c0..5c871a88d 100644 --- a/drivers/usb/mousedev.c +++ b/drivers/usb/mousedev.c @@ -1,5 +1,5 @@ /* - * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $ + * $Id: mousedev.c,v 1.10 2000/06/23 09:23:00 vojtech Exp $ * * Copyright (c) 1999-2000 Vojtech Pavlik * @@ -47,7 +47,7 @@ #endif struct mousedev { - int used; + int exist; int open; int minor; wait_queue_head_t wait; @@ -172,22 +172,30 @@ static int mousedev_release(struct inode * inode, struct file * file) struct input_handle *handle = mousedev_handler.handle; while (handle) { struct mousedev *mousedev = handle->private; - if (!mousedev->open) - input_close_device(handle); + if (!mousedev->open) { + if (mousedev->exist) { + input_close_device(&mousedev->handle); + } else { + input_unregister_minor(mousedev->devfs); + mousedev_table[mousedev->minor] = NULL; + kfree(mousedev); + } + } handle = handle->hnext; } } else { - if (!mousedev_mix.open) - input_close_device(&list->mousedev->handle); + if (!mousedev_mix.open) { + if (list->mousedev->exist) { + input_close_device(&list->mousedev->handle); + } else { + input_unregister_minor(list->mousedev->devfs); + mousedev_table[list->mousedev->minor] = NULL; + kfree(list->mousedev); + } + } } } - if (!--list->mousedev->used) { - input_unregister_minor(list->mousedev->devfs); - mousedev_table[list->mousedev->minor] = NULL; - kfree(list->mousedev); - } - kfree(list); return 0; @@ -210,20 +218,20 @@ static int mousedev_open(struct inode * inode, struct file * file) mousedev_table[i]->list = list; file->private_data = list; - list->mousedev->used++; - if (!list->mousedev->open++) { if (list->mousedev->minor == MOUSEDEV_MIX) { struct input_handle *handle = mousedev_handler.handle; while (handle) { struct mousedev *mousedev = handle->private; if (!mousedev->open) - input_open_device(handle); + if (mousedev->exist) + input_open_device(handle); handle = handle->hnext; } } else { - if (!mousedev_mix.open) - input_open_device(&list->mousedev->handle); + if (!mousedev_mix.open) + if (list->mousedev->exist) + input_open_device(&list->mousedev->handle); } } @@ -402,7 +410,7 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru memset(mousedev, 0, sizeof(struct mousedev)); init_waitqueue_head(&mousedev->wait); - mousedev->used = 1; + mousedev->exist = 1; mousedev->minor = minor; mousedev_table[minor] = mousedev; @@ -424,10 +432,13 @@ static void mousedev_disconnect(struct input_handle *handle) { struct mousedev *mousedev = handle->private; - if (mousedev->open || mousedev_mix.open) - input_close_device(handle); + mousedev->exist = 0; - if (!--mousedev->used) { + if (mousedev->open) { + input_close_device(handle); + } else { + if (mousedev_mix.open) + input_close_device(handle); input_unregister_minor(mousedev->devfs); mousedev_table[mousedev->minor] = NULL; kfree(mousedev); @@ -449,7 +460,7 @@ static int __init mousedev_init(void) memset(&mousedev_mix, 0, sizeof(struct mousedev)); init_waitqueue_head(&mousedev_mix.wait); mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; - mousedev_mix.used = 1; + mousedev_mix.exist = 1; mousedev_mix.minor = MOUSEDEV_MIX; mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index 80b82bba2..8c588f79e 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,11 +1,37 @@ /* ** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller ** -** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** Copyright (c) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** ** -** Distribute under GPL version 2 or later. +** ChangeLog: +** .... Most of the time spend reading sources & docs. +** v0.2.x First official release for the Linux kernel. +** v0.3.0 Beutified and structured, some bugs fixed. +** v0.3.x URBifying bulk requests and bugfixing. First relatively +** stable release. Still can touch device's registers only +** from top-halves. +** v0.4.0 Control messages remained unurbified are now URBs. +** Now we can touch the HW at any time. */ +/* + * 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 + */ + + #include <linux/module.h> #include <linux/sched.h> #include <linux/malloc.h> @@ -16,7 +42,7 @@ #include <linux/usb.h> -static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; +static const char *version = __FILE__ ": v0.4.0 2000/06/15 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; #define PEGASUS_MTU 1500 @@ -24,17 +50,39 @@ static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko #define SROM_WRITE 0x01 #define SROM_READ 0x02 #define PEGASUS_TX_TIMEOUT (HZ*5) +#define PEGASUS_CTRL_TIMEOUT 1000 #define PEGASUS_RESET 1 #define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) +enum pegasus_registers { + EthCtrl0 = 0, + EthCtrl1 = 1, + EthCtrl2 = 2, + EthID = 0x10, + EpromOffset = 0x20, + EpromData = 0x21, /* 0x21 low, 0x22 high byte */ + EpromCtrl = 0x23, + PhyAddr = 0x25, + PhyData = 0x26, /* 0x26 low, 0x27 high byte */ + PhyCtrl = 0x28, + UsbStst = 0x2a, + EthTxStat0 = 0x2b, + EthTxStat1 = 0x2c, + EthRxStat = 0x2d, + Gpio0 = 0x7e, + Gpio1 = 0x7f, +}; + + struct pegasus { struct usb_device *usb; struct net_device *net; struct net_device_stats stats; int flags; - spinlock_t pegasus_lock; - struct urb rx_urb, tx_urb, intr_urb; + spinlock_t pegasus_lock, ctrl_lock; + struct urb rx_urb, tx_urb, intr_urb, ctrl_urb; + devrequest dr; unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); unsigned char ALIGN(intr_buff[8]); @@ -64,7 +112,7 @@ static struct usb_eth_dev usb_dev_id[] = { {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, - {"D-Link DU-10", 0x07b8, 0xabc1, NULL}, + {"D-Link DU-E10", 0x07b8, 0xabc1, NULL}, {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, {"Linksys USB100TX", 0x066b, 0x2203, NULL}, {"Linksys USB100TX", 0x066b, 0x2204, NULL}, @@ -78,23 +126,120 @@ static struct usb_eth_dev usb_dev_id[] = { }; -#define pegasus_get_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ); -#define pegasus_set_registers(dev, indx, size, data)\ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); -#define pegasus_set_register(dev, indx, value) \ - { __u8 __data = value; \ - usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);} + +static void pegasus_ctrl_end( urb_t *urb ) +{ + if ( urb->status ) + warn("ctrl_urb end status %d", urb->status); +} -static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) +static int pegasus_ctrl_timeout( urb_t *ctrl_urb ) +{ + int timeout=0; + + while ( ctrl_urb->status == -EINPROGRESS ) { + if ( timeout++ < PEGASUS_CTRL_TIMEOUT ) { + udelay(100); + continue; + } + err("ctrl urb busy %d", ctrl_urb->status); + usb_unlink_urb( ctrl_urb ); + return ctrl_urb->status; + } + return 0; +} + + +static int pegasus_get_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0xc0; + pegasus->dr.request = 0xf0; + pegasus->dr.value = 0x0; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_rcvctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + data, size, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRLs %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_set_registers( struct pegasus *pegasus, __u16 indx, __u16 size, void *data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0x40; + pegasus->dr.request = 0xf1; + pegasus->dr.value = 0x0; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = size; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + data, size, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRL %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_set_register( struct pegasus *pegasus, __u16 indx,__u8 data ) +{ + int ret; + + + spin_lock( &pegasus->ctrl_lock ); + pegasus->dr.requesttype = 0x40; + pegasus->dr.request = 0xf1; + pegasus->dr.value = data; + pegasus->dr.index = indx; + pegasus->dr.length = pegasus->ctrl_urb.transfer_buffer_length = 1; + + FILL_CONTROL_URB( &pegasus->ctrl_urb, pegasus->usb, + usb_sndctrlpipe(pegasus->usb,0), (char *)&pegasus->dr, + &data, 1, pegasus_ctrl_end, pegasus ); + + if ( (ret = usb_submit_urb( &pegasus->ctrl_urb )) ) + err("BAD CTRL %d", ret); + else + ret = pegasus_ctrl_timeout( &pegasus->ctrl_urb ); + + spin_unlock( &pegasus->ctrl_lock ); + + return ret; +} + + +static int pegasus_read_phy_word(struct pegasus *pegasus, __u8 index, __u16 *regdata) { int i; __u8 data[4] = { 1, 0, 0, 0x40 + index }; - pegasus_set_registers(dev, 0x25, 4, data); + pegasus_set_registers(pegasus, PhyAddr, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x26, 3, data); + pegasus_get_registers(pegasus, PhyData, 3, data); if (data[2] & 0x80) { *regdata = *(__u16 *)(data); return 0; @@ -107,14 +252,14 @@ static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regd } -static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) +static int pegasus_write_phy_word(struct pegasus *pegasus, __u8 index, __u16 regdata) { int i; __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index }; - pegasus_set_registers(dev, 0x25, 4, data); + pegasus_set_registers(pegasus, PhyAddr, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x28, 1, data); + pegasus_get_registers(pegasus, PhyCtrl, 1, data); if (data[0] & 0x80) return 0; udelay(100); @@ -125,51 +270,51 @@ static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regd } -static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) +static int pegasus_rw_eprom_word(struct pegasus *pegasus, __u8 index, __u16 *retdata, __u8 direction) { int i; __u8 data[4] = { index, 0, 0, direction }; - pegasus_set_registers(dev, 0x20, 4, data); + pegasus_set_registers(pegasus, EpromOffset, 4, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 0x23, 1, data); + pegasus_get_registers(pegasus, EpromCtrl, 1, data); if (data[0] & 4) { - pegasus_get_registers(dev, 0x21, 2, data); + pegasus_get_registers(pegasus, EpromData, 2, data); *retdata = *(__u16 *)data; return 0; } } - warn("pegasus_rw_srom_word() failed"); + warn("pegasus_rw_eprom_word() failed"); return 1; } -static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) +static int pegasus_get_node_id(struct pegasus *pegasus, __u8 *id) { int i; for (i = 0; i < 3; i++) - if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ)) + if (pegasus_rw_eprom_word(pegasus,i,(__u16 *)&id[i*2],SROM_READ)) return 1; return 0; } -static int pegasus_reset_mac(struct usb_device *dev) +static int pegasus_reset_mac(struct pegasus *pegasus) { __u8 data = 0x8; int i; - pegasus_set_register(dev, 1, data); + pegasus_set_register(pegasus, EthCtrl1, data); for (i = 0; i < 100; i++) { - pegasus_get_registers(dev, 1, 1, &data); + pegasus_get_registers(pegasus, EthCtrl1, 1, &data); if (~data & 0x08) { if (loopback & 1) return 0; if (loopback & 2) - pegasus_write_phy_word(dev, 0, 0x4000); - pegasus_set_register(dev, 0x7e, 0x24); - pegasus_set_register(dev, 0x7e, 0x27); + pegasus_write_phy_word(pegasus, 0, 0x4000); + pegasus_set_register(pegasus, Gpio0, 0x24); + pegasus_set_register(pegasus, Gpio0, 0x27); return 0; } } @@ -183,22 +328,22 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) __u16 partmedia, temp; __u8 node_id[6]; __u8 data[4]; + struct pegasus *pegasus = dev->priv; - if (pegasus_get_node_id(usb, node_id)) + if (pegasus_get_node_id(pegasus, node_id)) return 1; - pegasus_set_registers(usb, 0x10, 6, node_id); + pegasus_set_registers(pegasus, EthID, 6, node_id); memcpy(dev->dev_addr, node_id, 6); - if (pegasus_read_phy_word(usb, 1, &temp)) + if (pegasus_read_phy_word(pegasus, 1, &temp)) return 2; if ((~temp & 4) && !loopback) { - warn("%s: link NOT established (0x%x), check the cable.", + warn("%s: link NOT established (0x%x) - check the cable.", dev->name, temp); - /* return 3; FIXME */ } - if (pegasus_read_phy_word(usb, 5, &partmedia)) + if (pegasus_read_phy_word(pegasus, 5, &partmedia)) return 4; if ((partmedia & 0x1f) != 1) { @@ -210,7 +355,7 @@ static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); data[2] = (loopback & 1) ? 0x09 : 0x01; - pegasus_set_registers(usb, 0, 3, data); + pegasus_set_registers(pegasus, EthCtrl0, 3, data); return 0; } @@ -281,24 +426,26 @@ static void pegasus_write_bulk(struct urb *urb) { struct pegasus *pegasus = urb->context; - spin_lock(&pegasus->pegasus_lock); if (urb->status) info("%s: TX status %d", pegasus->net->name, urb->status); +#if 1 /* Should be fixed */ + if (urb->status == -ETIMEDOUT) + pegasus_reset_mac(pegasus); +#endif netif_wake_queue(pegasus->net); - - spin_unlock(&pegasus->pegasus_lock); } static void pegasus_tx_timeout(struct net_device *net) { struct pegasus *pegasus = net->priv; - warn("%s: Tx timed out. Reseting...", net->name); + usb_unlink_urb(&pegasus->tx_urb); + warn("%s: Tx timed out. Reseting...", net->name); + pegasus_reset_mac( pegasus ); pegasus->stats.tx_errors++; net->trans_start = jiffies; - pegasus->flags |= PEGASUS_RESET; netif_wake_queue(net); } @@ -372,6 +519,8 @@ static int pegasus_close(struct net_device *net) netif_stop_queue(net); + if ( pegasus->ctrl_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->ctrl_urb); if ( pegasus->rx_urb.status == -EINPROGRESS ) usb_unlink_urb(&pegasus->rx_urb); if ( pegasus->tx_urb.status == -EINPROGRESS ) @@ -394,12 +543,12 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) case SIOCDEVPRIVATE: data[0] = 1; case SIOCDEVPRIVATE+1: - pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + pegasus_read_phy_word(pegasus, data[1] & 0x1f, &data[3]); return 0; case SIOCDEVPRIVATE+2: if (!capable(CAP_NET_ADMIN)) return -EPERM; - pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + pegasus_write_phy_word(pegasus, data[1] & 0x1f, data[2]); return 0; default: return -EOPNOTSUPP; @@ -410,19 +559,23 @@ static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) static void pegasus_set_rx_mode(struct net_device *net) { struct pegasus *pegasus = net->priv; + __u8 tmp; netif_stop_queue(net); if (net->flags & IFF_PROMISC) { info("%s: Promiscuous mode enabled", net->name); -/* pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */ + pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); + pegasus_set_register(pegasus, EthCtrl2, tmp | 4); } else if ((net->mc_count > multicast_filter_limit) || (net->flags & IFF_ALLMULTI)) { - pegasus_set_register(pegasus->usb, 0, 0xfa); - pegasus_set_register(pegasus->usb, 2, 0); + pegasus_set_register(pegasus, EthCtrl0, 0xfa); + pegasus_set_register(pegasus, EthCtrl2, 0); info("%s set allmulti", net->name); } else { info("%s: set Rx mode", net->name); + pegasus_get_registers(pegasus, EthCtrl2, 1, &tmp); + pegasus_set_register(pegasus, EthCtrl2, tmp & ~4); } netif_wake_queue(net); @@ -464,12 +617,6 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) } memset(pegasus, 0, sizeof(struct pegasus)); - if (pegasus_reset_mac(dev)) { - err("can't reset MAC"); - kfree(pegasus); - return NULL; - } - net = init_etherdev(0, 0); net->priv = pegasus; net->open = pegasus_open; @@ -485,6 +632,7 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) pegasus->usb = dev; pegasus->net = net; pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED; + pegasus->ctrl_lock = SPIN_LOCK_UNLOCKED; FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, @@ -495,7 +643,12 @@ static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); - + if (pegasus_reset_mac(pegasus)) { + err("can't reset MAC"); + kfree(pegasus); + return NULL; + } + printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); return pegasus; @@ -515,7 +668,9 @@ static void pegasus_disconnect(struct usb_device *dev, void *ptr) dev_close(pegasus->net); unregister_netdev(pegasus->net); - + + if ( pegasus->ctrl_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->ctrl_urb); if ( pegasus->rx_urb.status == -EINPROGRESS ) usb_unlink_urb(&pegasus->rx_urb); if ( pegasus->tx_urb.status == -EINPROGRESS ) diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c index b2e18bd8e..d60fd8e33 100644 --- a/drivers/usb/serial/usbserial.c +++ b/drivers/usb/serial/usbserial.c @@ -14,6 +14,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/23/2000) gkh + * Cleaned up debugging statements in a quest to find UHCI timeout bug. + * * (05/22/2000) gkh * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be * removed from the individual device source files. @@ -358,7 +361,7 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor) int i, j; int good_spot; - dbg("get_free_serial %d", num_ports); + dbg(__FUNCTION__ " %d", num_ports); *minor = 0; for (i = 0; i < SERIAL_TTY_MINORS; ++i) { @@ -373,14 +376,14 @@ static struct usb_serial *get_free_serial (int num_ports, int *minor) continue; if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) { - err("Out of memory"); + err(__FUNCTION__ " - Out of memory"); return NULL; } memset(serial, 0, sizeof(struct usb_serial)); serial->magic = USB_SERIAL_MAGIC; serial_table[i] = serial; *minor = i; - dbg("minor base = %d", *minor); + dbg(__FUNCTION__ " - minor base = %d", *minor); for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) serial_table[i] = serial; return serial; @@ -393,7 +396,7 @@ static void return_serial (struct usb_serial *serial) { int i; - dbg("return_serial"); + dbg(__FUNCTION__); if (serial == NULL) return; @@ -418,7 +421,7 @@ int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *da // dbg("ezusb_writememory %x, %d", address, length); if (!transfer_buffer) { - err("ezusb_writememory: kmalloc(%d) failed.", length); + err(__FUNCTION__ " - kmalloc(%d) failed.", length); return -ENOMEM; } memcpy (transfer_buffer, data, length); @@ -431,10 +434,10 @@ int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *da int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) { int response; - dbg("ezusb_set_reset: %d", reset_bit); + dbg(__FUNCTION__ " - %d", reset_bit); response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0); if (response < 0) { - err("ezusb_set_reset %d failed", reset_bit); + err(__FUNCTION__ "- %d failed", reset_bit); } return response; } @@ -451,7 +454,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) struct usb_serial_port *port; int portNumber; - dbg("serial_open"); + dbg(__FUNCTION__); /* initialize the pointer incase something fails */ tty->driver_data = NULL; @@ -459,7 +462,7 @@ static int serial_open (struct tty_struct *tty, struct file * filp) /* get the serial object associated with this tty pointer */ serial = get_serial_by_minor (MINOR(tty->device)); - if (serial_paranoia_check (serial, "serial_open")) { + if (serial_paranoia_check (serial, __FUNCTION__)) { return -ENODEV; } @@ -481,16 +484,16 @@ static int serial_open (struct tty_struct *tty, struct file * filp) static void serial_close(struct tty_struct *tty, struct file * filp) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_close"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not opened"); + dbg (__FUNCTION__ " - port not opened"); return; } @@ -506,16 +509,16 @@ static void serial_close(struct tty_struct *tty, struct file * filp) static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_write"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_write port %d, %d byte(s)", port->number, count); + dbg(__FUNCTION__ " - port %d, %d byte(s)", port->number, count); if (!port->active) { - dbg ("port not opened"); + dbg (__FUNCTION__ " - port not opened"); return -EINVAL; } @@ -531,16 +534,16 @@ static int serial_write (struct tty_struct * tty, int from_user, const unsigned static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_write_room"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_write_room port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -EINVAL; } @@ -556,14 +559,14 @@ static int serial_write_room (struct tty_struct *tty) static int serial_chars_in_buffer (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_chars_in_buffer"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -EINVAL; } @@ -579,16 +582,16 @@ static int serial_chars_in_buffer (struct tty_struct *tty) static void serial_throttle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_throttle"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_throttle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -604,16 +607,16 @@ static void serial_throttle (struct tty_struct * tty) static void serial_unthrottle (struct tty_struct * tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_unthrottle"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_unthrottle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -629,16 +632,16 @@ static void serial_unthrottle (struct tty_struct * tty) static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_ioctl"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return -ENODEV; } - dbg("serial_ioctl port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return -ENODEV; } @@ -654,16 +657,16 @@ static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned in static void serial_set_termios (struct tty_struct *tty, struct termios * old) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_set_termios"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_set_termios port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -679,16 +682,16 @@ static void serial_set_termios (struct tty_struct *tty, struct termios * old) static void serial_break (struct tty_struct *tty, int break_state) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; - struct usb_serial *serial = get_usb_serial (port, "serial_break"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); if (!serial) { return; } - dbg("serial_break port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!port->active) { - dbg ("port not open"); + dbg (__FUNCTION__ " - port not open"); return; } @@ -708,10 +711,10 @@ static int generic_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; - dbg("generic_open port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (port->active) { - dbg ("device already open"); + dbg (__FUNCTION__ " - device already open"); return -EINVAL; } port->active = 1; @@ -720,7 +723,7 @@ static int generic_open (struct usb_serial_port *port, struct file *filp) if (serial->num_bulk_in) { /*Start reading from the device*/ if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); } return (0); @@ -731,7 +734,7 @@ static void generic_close (struct usb_serial_port *port, struct file * filp) { struct usb_serial *serial = port->serial; - dbg("generic_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); /* shutdown any bulk reads that might be going on */ if (serial->num_bulk_out) { @@ -749,22 +752,33 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns { struct usb_serial *serial = port->serial; - dbg("generic_serial_write port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (count == 0) { - dbg("write request of 0 bytes"); + dbg(__FUNCTION__ " - write request of 0 bytes"); return (0); } /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) { - dbg ("already writing"); + dbg (__FUNCTION__ " - already writing"); return (0); } count = (count > port->bulk_out_size) ? port->bulk_out_size : count; +#ifdef DEBUG + { + int i; + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ " - length = %d, data = ", count); + for (i = 0; i < count; ++i) { + printk ("%.2x ", buf[i]); + } + printk ("\n"); + } +#endif + if (from_user) { copy_from_user(port->write_urb->transfer_buffer, buf, count); } @@ -776,7 +790,7 @@ static int generic_write (struct usb_serial_port *port, int from_user, const uns port->write_urb->transfer_buffer_length = count; if (usb_submit_urb(port->write_urb)) - dbg("usb_submit_urb(write bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(write bulk) failed"); return (count); } @@ -791,14 +805,14 @@ static int generic_write_room (struct usb_serial_port *port) struct usb_serial *serial = port->serial; int room; - dbg("generic_write_room port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) room = 0; else room = port->bulk_out_size; - dbg("generic_write_room returns %d", room); + dbg(__FUNCTION__ " returns %d", room); return (room); } @@ -810,7 +824,7 @@ static int generic_chars_in_buffer (struct usb_serial_port *port) { struct usb_serial *serial = port->serial; - dbg("generic_chars_in_buffer port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (serial->num_bulk_out) { if (port->write_urb->status == -EINPROGRESS) { @@ -825,23 +839,25 @@ static int generic_chars_in_buffer (struct usb_serial_port *port) static void generic_read_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = get_usb_serial (port, "generic_read_bulk_callback"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int i; + dbg (__FUNCTION__ " - enter"); + if (!serial) { return; } if (urb->status) { - dbg("nonzero read bulk status received: %d", urb->status); + dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status); return; } #ifdef DEBUG if (urb->actual_length) { - printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + printk (KERN_DEBUG __FILE__ ": " __FUNCTION__ "- length = %d, data = ", urb->actual_length); for (i = 0; i < urb->actual_length; ++i) { printk ("%.2x ", data[i]); } @@ -859,8 +875,10 @@ static void generic_read_bulk_callback (struct urb *urb) /* Continue trying to always read */ if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); + dbg(__FUNCTION__ " - failed resubmitting read urb"); + dbg (__FUNCTION__ " - exit"); + return; } @@ -868,15 +886,17 @@ static void generic_read_bulk_callback (struct urb *urb) static void generic_write_bulk_callback (struct urb *urb) { struct usb_serial_port *port = (struct usb_serial_port *)urb->context; - struct usb_serial *serial = get_usb_serial (port, "generic_write_bulk_callback"); + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; + dbg (__FUNCTION__ " - enter"); + if (!serial) { return; } if (urb->status) { - dbg("nonzero write bulk status received: %d", urb->status); + dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status); return; } @@ -886,6 +906,8 @@ static void generic_write_bulk_callback (struct urb *urb) wake_up_interruptible(&tty->write_wait); + dbg (__FUNCTION__ " - exit"); + return; } @@ -1255,7 +1277,7 @@ int usb_serial_init(void) serial_tty_driver.init_termios = tty_std_termios; serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; if (tty_register_driver (&serial_tty_driver)) { - err("failed to register tty driver"); + err(__FUNCTION__ " - failed to register tty driver"); return -1; } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 5cfb4c42a..8be2dd351 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -11,6 +11,9 @@ * * See Documentation/usb/usb-serial.txt for more information on using this driver * + * (06/23/2000) gkh + * Cleaned up debugging statements in a quest to find UHCI timeout bug. + * * (04/27/2000) Ryan VanderBijl * Fixed memory leak in visor_close * @@ -80,10 +83,10 @@ struct usb_serial_device_type handspring_device = { ******************************************************************************/ static int visor_open (struct usb_serial_port *port, struct file *filp) { - dbg("visor_open port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (port->active) { - dbg ("device already open"); + dbg (__FUNCTION__ " - device already open"); return -EINVAL; } @@ -91,7 +94,7 @@ static int visor_open (struct usb_serial_port *port, struct file *filp) /*Start reading from the device*/ if (usb_submit_urb(port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); return (0); } @@ -102,10 +105,10 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) struct usb_serial *serial = port->serial; unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL); - dbg("visor_close port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (!transfer_buffer) { - err("visor_close: kmalloc(%d) failed.", 0x12); + err(__FUNCTION__ " - kmalloc(%d) failed.", 0x12); } else { /* send a shutdown message to the device */ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, @@ -122,7 +125,7 @@ static void visor_close (struct usb_serial_port *port, struct file * filp) static void visor_throttle (struct usb_serial_port *port) { - dbg("visor_throttle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); usb_unlink_urb (port->read_urb); @@ -132,10 +135,10 @@ static void visor_throttle (struct usb_serial_port *port) static void visor_unthrottle (struct usb_serial_port *port) { - dbg("visor_unthrottle port %d", port->number); + dbg(__FUNCTION__ " - port %d", port->number); if (usb_unlink_urb (port->read_urb)) - dbg("usb_submit_urb(read bulk) failed"); + dbg(__FUNCTION__ " - usb_submit_urb(read bulk) failed"); return; } @@ -148,20 +151,20 @@ static int visor_startup (struct usb_serial *serial) unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL); if (!transfer_buffer) { - err("visor_startup: kmalloc(%d) failed.", 256); + err(__FUNCTION__ " - kmalloc(%d) failed.", 256); return -ENOMEM; } - dbg("visor_startup"); + dbg(__FUNCTION__); - dbg("visor_setup: Set config to 1"); + dbg(__FUNCTION__ " - Set config to 1"); usb_set_configuration (serial->dev, 1); /* send a get connection info request */ response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION, 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); if (response < 0) { - err("visor_startup: error getting connection information"); + err(__FUNCTION__ " - error getting connection information"); } else { struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer; char *string; @@ -195,7 +198,7 @@ static int visor_startup (struct usb_serial *serial) response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300); if (response < 0) { - err("visor_startup: error getting bytes available request"); + err(__FUNCTION__ " - error getting bytes available request"); } kfree (transfer_buffer); diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index b9cead4fe..6d88d360b 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -1,11 +1,16 @@ /* Driver for USB Mass Storage compliant devices * - * Initial work by: - * (c) 1999 Michael Gee (michael@linuxspecific.com) + * $Id: usb-storage.c,v 1.11 2000/06/20 03:19:31 mdharm Exp $ * * Current development and maintainance by: * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) * + * Developed with the assistance of: + * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * * This driver is based on the 'USB Mass Storage Class' document. This * describes in detail the protocol used to communicate with such * devices. Clearly, the designers had SCSI and ATAPI commands in @@ -22,6 +27,20 @@ * * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more * information about this driver. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * 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 <linux/module.h> @@ -88,6 +107,7 @@ struct us_data { char *protocol_name; u8 subclass; u8 protocol; + u8 max_lun; /* information about the device -- only good if device is attached */ u8 ifnum; /* interface number */ @@ -536,8 +556,9 @@ static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us) /* save the old command */ memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE); + /* set the command and the LUN */ srb->cmnd[0] = REQUEST_SENSE; - srb->cmnd[1] = 0; + srb->cmnd[1] = old_cmnd[1] & 0xE0; srb->cmnd[2] = 0; srb->cmnd[3] = 0; srb->cmnd[4] = 18; @@ -791,7 +812,7 @@ static int Bulk_max_lun(struct us_data *us) result, data); /* if we have a successful request, return the result */ - if (!result) + if (result == 1) return data; /* if we get a STALL, clear the stall */ @@ -839,6 +860,9 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) if (result == -EPIPE) { US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); usb_clear_halt(us->pusb_dev, pipe); + } else if (result) { + /* unknown error -- we've got a problem */ + return USB_STOR_TRANSPORT_ERROR; } /* if the command transfered well, then we go to the data stage */ @@ -976,25 +1000,17 @@ static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) srb->cmnd[0] = srb->cmnd[0] | 0x20; break; } /* end switch on cmnd[0] */ + + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); /* send the command to the transport layer */ invoke_transport(srb, us); - /* Fix the MODE_SENSE data if we translated the command - */ - if (old_cmnd == MODE_SENSE) { - unsigned char *dta = (unsigned char *)us->srb->request_buffer; - - /* FIXME: we need to compress the entire data structure here - */ - dta[0] = dta[1]; /* data len */ - dta[1] = dta[2]; /* med type */ - dta[2] = dta[3]; /* dev-spec prm */ - dta[3] = dta[7]; /* block desc len */ - printk (KERN_DEBUG USB_STORAGE - "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", - dta[0], dta[1], dta[2], dta[3]); - } + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); /* Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us @@ -1084,24 +1100,16 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) break; } /* end switch on cmnd[0] */ + /* convert MODE_SELECT data here */ + if (old_cmnd == MODE_SELECT) + usb_stor_scsiSense6to10(srb); + /* send the command to the transport layer */ invoke_transport(srb, us); - /* Fix the MODE_SENSE data here if we had to translate the command - */ - if (old_cmnd == MODE_SENSE) { - unsigned char *dta = (unsigned char *)us->srb->request_buffer; - - /* FIXME: we need to compress the entire data structure here - */ - dta[0] = dta[1]; /* data len */ - dta[1] = dta[2]; /* med type */ - dta[2] = dta[3]; /* dev-spec prm */ - dta[3] = dta[7]; /* block desc len */ - printk (KERN_DEBUG USB_STORAGE - "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", - dta[0], dta[1], dta[2], dta[3]); - } + /* Fix the MODE_SENSE data if we translated the command */ + if ((old_cmnd == MODE_SENSE) && (srb->result == GOOD)) + usb_stor_scsiSense10to6(srb); /* Fix-up the return data from an INQUIRY command to show * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us @@ -1310,9 +1318,8 @@ static int us_release(struct Scsi_Host *psh) down(&(us->notify)); /* free the data structure we were using */ - US_DEBUGP("-- freeing private host data structure\n"); + US_DEBUGP("-- freeing URB\n"); kfree(us->current_urb); - kfree(us); (struct us_data*)psh->hostdata[0] = NULL; /* we always have a successful release */ @@ -1536,11 +1543,10 @@ static int usb_stor_control_thread(void * __us) switch (action) { case US_ACT_COMMAND: - /* reject if target != 0 or if single-lun device - * and LUN != 0 + /* reject if target != 0 or if LUN is higher than + * the maximum known LUN */ - if (us->srb->target || - ((us->flags & US_FL_SINGLE_LUN) && us->srb->lun)) { + if (us->srb->target || (us->srb->lun > us->max_lun)) { US_DEBUGP("Bad device number (%d/%d)\n", us->srb->target, us->srb->lun); @@ -1623,32 +1629,37 @@ static int usb_stor_control_thread(void * __us) /* This is the list of devices we recognize, along with their flag data */ static struct us_unusual_dev us_unusual_dev_list[] = { - { 0x03f0, 0x0107, 0x0200, - "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, - { 0x04e6, 0x0001, 0x0200, - "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x04e6, 0x0002, 0x0100, - "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x04e6, 0x0006, 0x0100, - "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x057b, 0x0000, 0x0114, - "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, - { 0x059b, 0x0030, 0x0100, - "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN}, - { 0x0693, 0x0002, 0x0100, - "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK, - US_FL_ALT_LENGTH}, - { 0x0781, 0x0001, 0x0200, - "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, - US_FL_SINGLE_LUN | US_FL_START_STOP}, - { 0x0781, 0x0002, 0x0009, - "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, - US_FL_SINGLE_LUN | US_FL_IGNORE_SER}, - { 0x07af, 0x0005, 0x0100, - "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, - { 0x0000, 0x0000, 0x0, - "", 0, 0, 0} -}; + { 0x03f0, 0x0107, 0x0200, 0x0200, "HP USB CD-Writer Plus", + US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, 0x0200, "Matshita LS-120", + US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, 0x0100, "Shuttle eUSCSI Bridge", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, 0x0100, "Shuttle eUSB MMC Adapter", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x054c, 0x0010, 0x0210, 0x0210, "Sony DSC-S30", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, + { 0x054c, 0x002d, 0x0100, 0x0100, "Sony Memorystick MSAC-US1", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP | + US_FL_MODE_XLATE | US_FL_ALT_LENGTH}, + { 0x057b, 0x0000, 0x0000, 0x0299, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x057b, 0x0000, 0x0300, 0x9999, "Y-E Data Flashbuster-U", + US_SC_UFI, US_PR_CBI, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, 0x0100, "Hagiwara FlashGate SmartMedia", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk ImageMate (SDDR-01)", + US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN | US_FL_START_STOP}, + { 0x0781, 0x0002, 0x0009, 0x0009, "Sandisk Imagemate (SDDR-31)", + US_SC_SCSI, US_PR_BULK, US_FL_IGNORE_SER}, + { 0x07af, 0x0005, 0x0100, 0x0100, "Microtech USB-SCSI-HD50", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x05ab, 0x0031, 0x0100, 0x0100, "In-System USB/IDE Bridge", + US_SC_8070, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0693, 0x0005, 0x0100, 0x0100, "Hagiwara Flashgate", + US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0 }}; /* Search our ususual device list, based on vendor/product combinations * to see if we can support this device. Returns a pointer to a structure @@ -1667,7 +1678,8 @@ static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, while ((ptr->idVendor != 0x0000) && !((ptr->idVendor == idVendor) && (ptr->idProduct == idProduct) && - (ptr->bcdDevice == bcdDevice))) + (ptr->bcdDeviceMin <= bcdDevice) && + (ptr->bcdDeviceMax >= bcdDevice))) ptr++; /* if the search ended because we hit the end record, we failed */ @@ -1968,20 +1980,21 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) ss->transport_name = "Control/Bulk"; ss->transport = CB_transport; ss->transport_reset = CB_reset; + ss->max_lun = 7; break; case US_PR_CBI: ss->transport_name = "Control/Bulk/Interrupt"; ss->transport = CBI_transport; ss->transport_reset = CB_reset; + ss->max_lun = 7; break; case US_PR_BULK: ss->transport_name = "Bulk"; ss->transport = Bulk_transport; ss->transport_reset = Bulk_reset; - /* FIXME: for testing purposes only */ - Bulk_max_lun(ss); + ss->max_lun = Bulk_max_lun(ss); break; default: @@ -1994,6 +2007,10 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum) } US_DEBUGP("Transport: %s\n", ss->transport_name); + /* fix for single-lun devices */ + if (ss->flags & US_FL_SINGLE_LUN) + ss->max_lun = 0; + switch (ss->subclass) { case US_SC_RBC: ss->protocol_name = "Reduced Block Commands (RBC)"; @@ -2134,6 +2151,576 @@ static void storage_disconnect(struct usb_device *dev, void *ptr) up(&(ss->dev_semaphore)); } +/************************************************************** + **************************************************************/ + +#define USB_STOR_SCSI_SENSE_HDRSZ 4 +#define USB_STOR_SCSI_SENSE_10_HDRSZ 8 + +struct usb_stor_scsi_sense_hdr +{ + __u8* dataLength; + __u8* mediumType; + __u8* devSpecParms; + __u8* blkDescLength; +}; + +typedef struct usb_stor_scsi_sense_hdr Usb_Stor_Scsi_Sense_Hdr; + +union usb_stor_scsi_sense_hdr_u +{ + Usb_Stor_Scsi_Sense_Hdr hdr; + __u8* array[USB_STOR_SCSI_SENSE_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_u Usb_Stor_Scsi_Sense_Hdr_u; + +struct usb_stor_scsi_sense_hdr_10 +{ + __u8* dataLengthMSB; + __u8* dataLengthLSB; + __u8* mediumType; + __u8* devSpecParms; + __u8* reserved1; + __u8* reserved2; + __u8* blkDescLengthMSB; + __u8* blkDescLengthLSB; +}; + +typedef struct usb_stor_scsi_sense_hdr_10 Usb_Stor_Scsi_Sense_Hdr_10; + +union usb_stor_scsi_sense_hdr_10_u +{ + Usb_Stor_Scsi_Sense_Hdr_10 hdr; + __u8* array[USB_STOR_SCSI_SENSE_10_HDRSZ]; +}; + +typedef union usb_stor_scsi_sense_hdr_10_u Usb_Stor_Scsi_Sense_Hdr_10_u; + +void usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* , Usb_Stor_Scsi_Sense_Hdr_u*, + Usb_Stor_Scsi_Sense_Hdr_10_u*, int* ); +void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ); + +int +usb_stor_scsiSense10to6( Scsi_Cmnd* the10 ) +{ + __u8 *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int sgLength=0; + +#if 0 + /* Make sure we get a MODE_SENSE_10 command */ + if ( the10->cmnd[0] != MODE_SENSE_10 ) + { + printk( KERN_ERR USB_STORAGE + "Scsi_Cmnd was not a MODE_SENSE_10.\n" ); + return -1; + } + + /* Now start to format the output */ + the10->cmnd[0] = MODE_SENSE; +#endif + US_DEBUGP("-- converting 10 byte sense data to 6 byte\n"); + the10->cmnd[0] = the10->cmnd[0] & 0xBF; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the10, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the10Locations.hdr.dataLengthLSB; + outputBufferSize += USB_STOR_SCSI_SENSE_HDRSZ; + + /* Check to see if we need to truncate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE_10 buffer into MODE_SENSE.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Data length */ + if ( *the10Locations.hdr.dataLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.dataLength = 0xff; + } + else + { + *the6Locations.hdr.dataLength = *the10Locations.hdr.dataLengthLSB; + } + + /* Medium type and DevSpecific parms */ + *the6Locations.hdr.mediumType = *the10Locations.hdr.mediumType; + *the6Locations.hdr.devSpecParms = *the10Locations.hdr.devSpecParms; + + /* Block descriptor length */ + if ( *the10Locations.hdr.blkDescLengthMSB != 0 ) /* MSB must be zero */ + { + printk( KERN_WARNING USB_STORAGE + "Command will be truncated to fit in SENSE6 buffer.\n" ); + *the6Locations.hdr.blkDescLength = 0xff; + } + else + { + *the6Locations.hdr.blkDescLength = *the10Locations.hdr.blkDescLengthLSB; + } + + if ( the10->use_sg == 0 ) + { + buffer = the10->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + outputBufferSize - USB_STOR_SCSI_SENSE_HDRSZ ); + /* initialise last bytes left in buffer due to smaller header */ + memset( &(buffer[outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ)]), + 0, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the10->request_buffer; + /* scan through this scatterlist and figure out starting positions */ + for ( i=0; i < the10->use_sg; i++) + { + sgLength = sg[i].length; + for ( j=0; j<sgLength; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + db=i; + di=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=sgLength; + i=the10->use_sg; + } + element++; + } + } + + /* Now we know where to start the copy from */ + element = USB_STOR_SCSI_SENSE_HDRSZ; + while ( element < outputBufferSize + -(USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + /* check limits */ + if ( sb >= the10->use_sg || + si >= sg[sb].length || + db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + + /* get next source */ + if ( sg[sb].length-1 == si ) + { + sb++; + si=0; + } + else + { + si++; + } + + element++; + } + /* zero the remaining bytes */ + while ( element < outputBufferSize ) + { + /* check limits */ + if ( db >= the10->use_sg || + di >= sg[db].length ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = 0; + + /* get next destination */ + if ( sg[db].length-1 == di ) + { + db++; + di=0; + } + else + { + di++; + } + element++; + } + } + + /* All done any everything was fine */ + return 0; +} + +int +usb_stor_scsiSense6to10( Scsi_Cmnd* the6 ) +{ + /* will be used to store part of buffer */ + __u8 tempBuffer[USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ], + *buffer=0; + int outputBufferSize = 0; + int length=0; + struct scatterlist *sg = 0; + int i=0, j=0, element=0; + Usb_Stor_Scsi_Sense_Hdr_u the6Locations; + Usb_Stor_Scsi_Sense_Hdr_10_u the10Locations; + int sb=0,si=0,db=0,di=0; + int lsb=0,lsi=0,ldb=0,ldi=0; + +#if 0 + /* Make sure we get a MODE_SENSE command */ + if ( the6->cmnd[0] != MODE_SENSE ) + { + printk( KERN_ERR USB_STORAGE + "Scsi_Cmnd was not MODE_SENSE.\n" ); + return -1; + } + + /* Now start to format the output */ + the6->cmnd[0] = MODE_SENSE_10; +#endif + US_DEBUGP("-- converting 6 byte sense data to 10 byte\n"); + the6->cmnd[0] = the6->cmnd[0] | 0x40; + + /* Determine buffer locations */ + usb_stor_scsiSenseParseBuffer( the6, &the6Locations, &the10Locations, + &length ); + + /* Work out minimum buffer to output */ + outputBufferSize = *the6Locations.hdr.dataLength; + outputBufferSize += USB_STOR_SCSI_SENSE_10_HDRSZ; + + /* Check to see if we need to trucate the output */ + if ( outputBufferSize > length ) + { + printk( KERN_WARNING USB_STORAGE + "Had to truncate MODE_SENSE into MODE_SENSE_10 buffer.\n" ); + printk( KERN_WARNING USB_STORAGE + "outputBufferSize is %d and length is %d.\n", + outputBufferSize, length ); + } + outputBufferSize = length; + + /* Block descriptor length - save these before overwriting */ + tempBuffer[2] = *the10Locations.hdr.blkDescLengthMSB; + tempBuffer[3] = *the10Locations.hdr.blkDescLengthLSB; + *the10Locations.hdr.blkDescLengthLSB = *the6Locations.hdr.blkDescLength; + *the10Locations.hdr.blkDescLengthMSB = 0; + + /* reserved - save these before overwriting */ + tempBuffer[0] = *the10Locations.hdr.reserved1; + tempBuffer[1] = *the10Locations.hdr.reserved2; + *the10Locations.hdr.reserved1 = *the10Locations.hdr.reserved2 = 0; + + /* Medium type and DevSpecific parms */ + *the10Locations.hdr.devSpecParms = *the6Locations.hdr.devSpecParms; + *the10Locations.hdr.mediumType = *the6Locations.hdr.mediumType; + + /* Data length */ + *the10Locations.hdr.dataLengthLSB = *the6Locations.hdr.dataLength; + *the10Locations.hdr.dataLengthMSB = 0; + + if ( !the6->use_sg ) + { + buffer = the6->request_buffer; + /* Copy the rest of the data */ + memmove( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + &(buffer[USB_STOR_SCSI_SENSE_HDRSZ]), + outputBufferSize-USB_STOR_SCSI_SENSE_10_HDRSZ ); + /* Put the first four bytes (after header) in place */ + memcpy( &(buffer[USB_STOR_SCSI_SENSE_10_HDRSZ]), + tempBuffer, + USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ ); + } + else + { + sg = (struct scatterlist *) the6->request_buffer; + /* scan through this scatterlist and figure out ending positions */ + for ( i=0; i < the6->use_sg; i++) + { + for ( j=0; j<sg[i].length; j++ ) + { + /* get to end of header */ + if ( element == USB_STOR_SCSI_SENSE_HDRSZ ) + { + ldb=i; + ldi=j; + } + if ( element == USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + lsb=i; + lsi=j; + /* we've found both sets now, exit loops */ + j=sg[i].length; + i=the6->use_sg; + break; + } + element++; + } + } + /* scan through this scatterlist and figure out starting positions */ + element = length-1; + /* destination is the last element */ + db=the6->use_sg-1; + di=sg[db].length-1; + for ( i=the6->use_sg-1; i >= 0; i--) + { + for ( j=sg[i].length-1; j>=0; j-- ) + { + /* get to end of header and find source for copy */ + if ( element == length - 1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ) ) + { + sb=i; + si=j; + /* we've found both sets now, exit loops */ + j=-1; + i=-1; + } + element--; + } + } + /* Now we know where to start the copy from */ + element = length-1 + - (USB_STOR_SCSI_SENSE_10_HDRSZ-USB_STOR_SCSI_SENSE_HDRSZ); + while ( element >= USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* check limits */ + if ( ( sb <= lsb && si < lsi ) || + ( db <= ldb && di < ldi ) ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + /* copy one byte */ + sg[db].address[di] = sg[sb].address[si]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + + /* get next source */ + if ( si == 0 ) + { + sb--; + si=sg[sb].length-1; + } + else + { + si--; + } + + element--; + } + /* copy the remaining four bytes */ + while ( element >= USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* check limits */ + if ( db <= ldb && di < ldi ) + { + printk( KERN_ERR USB_STORAGE + "Buffer overrun averted, this shouldn't happen!\n" ); + break; + } + + sg[db].address[di] = tempBuffer[element-USB_STOR_SCSI_SENSE_HDRSZ]; + + /* get next destination */ + if ( di == 0 ) + { + db--; + di=sg[db].length-1; + } + else + { + di--; + } + element--; + } + } + + /* All done and everything was fine */ + return 0; +} + +void +usb_stor_scsiSenseParseBuffer( Scsi_Cmnd* srb, Usb_Stor_Scsi_Sense_Hdr_u* the6, + Usb_Stor_Scsi_Sense_Hdr_10_u* the10, + int* length_p ) + +{ + int i = 0, j=0, element=0; + struct scatterlist *sg = 0; + int length = 0; + __u8* buffer=0; + + /* are we scatter-gathering? */ + if ( srb->use_sg != 0 ) + { + /* loop over all the scatter gather structures and + * get pointer to the data members in the headers + * (also work out the length while we're here) + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + { + length += sg[i].length; + /* We only do the inner loop for the headers */ + if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* scan through this scatterlist */ + for ( j=0; j<sg[i].length; j++ ) + { + if ( element < USB_STOR_SCSI_SENSE_HDRSZ ) + { + /* fill in the pointers for both header types */ + the6->array[element] = &(sg[i].address[j]); + the10->array[element] = &(sg[i].address[j]); + } + else if ( element < USB_STOR_SCSI_SENSE_10_HDRSZ ) + { + /* only the longer headers still cares now */ + the10->array[element] = &(sg[i].address[j]); + } + /* increase element counter */ + element++; + } + } + } + } + else + { + length = srb->request_bufflen; + buffer = srb->request_buffer; + if ( length < USB_STOR_SCSI_SENSE_10_HDRSZ ) + printk( KERN_ERR USB_STORAGE + "Buffer length smaller than header!!" ); + for( i=0; i<USB_STOR_SCSI_SENSE_10_HDRSZ; i++ ) + { + if ( i < USB_STOR_SCSI_SENSE_HDRSZ ) + { + the6->array[i] = &(buffer[i]); + the10->array[i] = &(buffer[i]); + } + else + { + the10->array[i] = &(buffer[i]); + } + } + } + + /* Set value of length passed in */ + *length_p = length; +} + +void +usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd ) +{ + int i=0, bufferSize = cmd->request_bufflen; + __u8* buffer = cmd->request_buffer; + struct scatterlist* sg = (struct scatterlist*)cmd->request_buffer; + + printk( KERN_ERR "Dumping information about %p.\n", cmd ); + printk( KERN_ERR "cmd->cmnd[0] value is %d.\n", cmd->cmnd[0] ); + printk( KERN_ERR "(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n", + MODE_SENSE, MODE_SENSE_10 ); + + printk( KERN_ERR "buffer is %p with length %d.\n", buffer, bufferSize ); + for ( i=0; i<bufferSize; i+=16 ) + { + printk( KERN_ERR "%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", + buffer[i], + buffer[i+1], + buffer[i+2], + buffer[i+3], + buffer[i+4], + buffer[i+5], + buffer[i+6], + buffer[i+7], + buffer[i+8], + buffer[i+9], + buffer[i+10], + buffer[i+11], + buffer[i+12], + buffer[i+13], + buffer[i+14], + buffer[i+15] ); + } + + printk( KERN_ERR "Buffer has %d scatterlists.\n", cmd->use_sg ); + for ( i=0; i<cmd->use_sg; i++ ) + { + printk( KERN_ERR "Length of scatterlist %d is %d.\n", i, sg[i].length ); + printk( KERN_ERR "%2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n", + sg[i].address[0], + sg[i].address[1], + sg[i].address[2], + sg[i].address[3], + sg[i].address[4], + sg[i].address[5], + sg[i].address[6], + sg[i].address[7], + sg[i].address[8], + sg[i].address[9], + sg[i].address[10], + sg[i].address[11], + sg[i].address[12], + sg[i].address[13], + sg[i].address[14], + sg[i].address[15] ); + } +} + +/************************************************************** + **************************************************************/ /*********************************************************************** * Initialization and registration @@ -2183,6 +2770,11 @@ void __exit usb_stor_exit(void) US_DEBUGP("-- calling scsi_unregister_module()\n"); scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt)); + /* Now that scsi_unregister_module is done with the host + * template, we can free the us_data structure (the host + * template is inline in this structure). */ + kfree (us_list); + /* advance the list pointer */ us_list = next; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 6e54b059b..82475f094 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -139,7 +139,8 @@ struct us_unusual_dev { /* we search the list based on these parameters */ __u16 idVendor; __u16 idProduct; - __u16 bcdDevice; + __u16 bcdDeviceMin; + __u16 bcdDeviceMax; /* the list specifies these parameters */ const char* name; diff --git a/drivers/usb/wmforce.c b/drivers/usb/wmforce.c deleted file mode 100644 index dff963b74..000000000 --- a/drivers/usb/wmforce.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ - * - * Copyright (c) 2000 Vojtech Pavlik - * - * USB Logitech WingMan Force joystick support - * - * Sponsored by SuSE - */ - -/* - * 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 - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail: - * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic - */ - -#include <linux/kernel.h> -#include <linux/malloc.h> -#include <linux/input.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/usb.h> - -MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 - -struct wmforce { - signed char data[8]; - struct input_dev dev; - struct urb irq; - int open; -}; - -static struct { - __s32 x; - __s32 y; -} wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -static char *wmforce_name = "Logitech WingMan Force"; - -static void wmforce_irq(struct urb *urb) -{ - struct wmforce *wmforce = urb->context; - unsigned char *data = wmforce->data; - struct input_dev *dev = &wmforce->dev; - - if (urb->status) return; - - if (data[0] != 1) { - if (data[0] != 2) - warn("received unknown report #%d", data[0]); - return; - } - - input_report_abs(dev, ABS_X, (__s16) (((__s16)data[2] << 8) | data[1])); - input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[4] << 8) | data[3])); - input_report_abs(dev, ABS_THROTTLE, data[5]); - input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x); - input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y); - - input_report_key(dev, BTN_TRIGGER, data[6] & 0x01); - input_report_key(dev, BTN_TOP, data[6] & 0x02); - input_report_key(dev, BTN_THUMB, data[6] & 0x04); - input_report_key(dev, BTN_TOP2, data[6] & 0x08); - input_report_key(dev, BTN_BASE, data[6] & 0x10); - input_report_key(dev, BTN_BASE2, data[6] & 0x20); - input_report_key(dev, BTN_BASE3, data[6] & 0x40); - input_report_key(dev, BTN_BASE4, data[6] & 0x80); - input_report_key(dev, BTN_BASE5, data[7] & 0x01); -} - -static int wmforce_open(struct input_dev *dev) -{ - struct wmforce *wmforce = dev->private; - - if (wmforce->open++) - return 0; - - if (usb_submit_urb(&wmforce->irq)) - return -EIO; - - return 0; -} - -static void wmforce_close(struct input_dev *dev) -{ - struct wmforce *wmforce = dev->private; - - if (!--wmforce->open) - usb_unlink_urb(&wmforce->irq); -} - -static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum) -{ - struct usb_endpoint_descriptor *endpoint; - struct wmforce *wmforce; - int i; - - if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || - dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) - return NULL; - - endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; - - if (!(wmforce = kmalloc(sizeof(struct wmforce), GFP_KERNEL))) return NULL; - memset(wmforce, 0, sizeof(struct wmforce)); - - wmforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - wmforce->dev.keybit[LONG(BTN_JOYSTICK)] = BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | - BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); - wmforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); - - for (i = ABS_X; i <= ABS_Y; i++) { - wmforce->dev.absmax[i] = 1920; - wmforce->dev.absmin[i] = -1920; - wmforce->dev.absflat[i] = 128; - } - - wmforce->dev.absmax[ABS_THROTTLE] = 255; - wmforce->dev.absmin[ABS_THROTTLE] = 0; - - for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { - wmforce->dev.absmax[i] = 1; - wmforce->dev.absmin[i] = -1; - } - - wmforce->dev.private = wmforce; - wmforce->dev.open = wmforce_open; - wmforce->dev.close = wmforce_close; - - wmforce->dev.name = wmforce_name; - wmforce->dev.idbus = BUS_USB; - wmforce->dev.idvendor = dev->descriptor.idVendor; - wmforce->dev.idproduct = dev->descriptor.idProduct; - wmforce->dev.idversion = dev->descriptor.bcdDevice; - - FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), - wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval); - - input_register_device(&wmforce->dev); - - printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", - wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum); - - return wmforce; -} - -static void wmforce_disconnect(struct usb_device *dev, void *ptr) -{ - struct wmforce *wmforce = ptr; - usb_unlink_urb(&wmforce->irq); - input_unregister_device(&wmforce->dev); - kfree(wmforce); -} - -static struct usb_driver wmforce_driver = { - name: "wmforce", - probe: wmforce_probe, - disconnect: wmforce_disconnect, -}; - -static int __init wmforce_init(void) -{ - usb_register(&wmforce_driver); - return 0; -} - -static void __exit wmforce_exit(void) -{ - usb_deregister(&wmforce_driver); -} - -module_init(wmforce_init); -module_exit(wmforce_exit); diff --git a/drivers/video/Config.in b/drivers/video/Config.in index 83e5dace8..6529b53c5 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -120,6 +120,7 @@ if [ "$CONFIG_FB" = "y" ]; then tristate ' ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY tristate ' ATI Rage 128 display support (EXPERIMENTAL)' CONFIG_FB_ATY128 bool ' 3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX + tristate ' SIS 630/540 display support (EXPERIMENTAL)' CONFIG_FB_SIS fi fi if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index ee1248a23..864e30f0c 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -654,8 +654,8 @@ register_framebuffer(struct fb_info *fb_info) } sprintf (name_buf, "%d", i); fb_info->devfs_handle = - devfs_register (devfs_handle, name_buf, 0, DEVFS_FL_NONE, - FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, 0, 0, + devfs_register (devfs_handle, name_buf, DEVFS_FL_DEFAULT, + FB_MAJOR, i, S_IFCHR | S_IRUGO | S_IWUGO, &fb_fops, NULL); return 0; diff --git a/drivers/video/sisfb.c b/drivers/video/sisfb.c index 7b83f69c4..d42448fbf 100644 --- a/drivers/video/sisfb.c +++ b/drivers/video/sisfb.c @@ -43,9 +43,10 @@ #define FALSE 0 #define TRUE 1 -/* Draw Function */ +/* Draw Function #define FBIOGET_GLYPH 0x4620 #define FBIOGET_HWCINFO 0x4621 +*/ #define BR(x) (0x8200 | (x) << 2) #define BITBLT 0x00000000 @@ -115,7 +116,8 @@ #define MMIO_SIZE 0x20000 /* 128K MMIO capability */ #define MAX_ROM_SCAN 0x10000 -#define RESERVED_MEM_SIZE 0x400000 /* 4M */ +#define RESERVED_MEM_SIZE_4M 0x400000 /* 4M */ +#define RESERVED_MEM_SIZE_8M 0x800000 /* 8M */ /* Mode set stuff */ #define DEFAULT_MODE 0 @@ -173,9 +175,9 @@ static struct board { const char *name; } dev_list[] = { { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_300, "SIS 300"}, { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_540, "SIS 540"}, { - PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS_630, "SIS 630"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_300, "SIS 300"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_540, "SIS 540"}, { + PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, "SIS 630"}, { 0, 0, NULL} }; @@ -1020,8 +1022,11 @@ static int sisfb_heap_init(void) struct OH *poh; u8 jTemp, tq_state; - heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE; - //heap_start = (unsigned long)ivideo.video_vbase + (video_size - RESERVED_MEM_SIZE); + if(ivideo.video_size > 0x800000) /* video ram is large than 8M */ + heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE_8M; + else + heap_start = (unsigned long) ivideo.video_vbase + RESERVED_MEM_SIZE_4M; + heap_end = (unsigned long) ivideo.video_vbase + ivideo.video_size; heap_size = heap_end - heap_start; @@ -1398,6 +1403,7 @@ static u32 get_reg3(u16 port) static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo) { +#if 0 unsigned char ModeID; u16 modeidlength; u16 usModeIDOffset; @@ -1411,6 +1417,8 @@ static u16 get_modeID_length(unsigned long ROMAddr, u16 ModeNo) ModeID = *((unsigned char *) (ROMAddr + usModeIDOffset)); } return (modeidlength); +#endif + return(10); } static int search_modeID(unsigned long ROMAddr, u16 ModeNo) @@ -2467,7 +2475,11 @@ static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, strcpy(fix->id, fb_info.modename); fix->smem_start = ivideo.video_base; - fix->smem_len = RESERVED_MEM_SIZE; /* reserved for Xserver */ + if(ivideo.video_size > 0x800000) + fix->smem_len = RESERVED_MEM_SIZE_8M; /* reserved for Xserver */ + else + fix->smem_len = RESERVED_MEM_SIZE_4M; /* reserved for Xserver */ + fix->type = video_type; fix->type_aux = 0; if (ivideo.video_bpp == 8) |