You can subscribe to this list here.
2000 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(19) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2001 |
Jan
(13) |
Feb
(12) |
Mar
(14) |
Apr
(3) |
May
(25) |
Jun
|
Jul
(9) |
Aug
|
Sep
(47) |
Oct
(24) |
Nov
(23) |
Dec
(58) |
2002 |
Jan
(87) |
Feb
(54) |
Mar
(38) |
Apr
(6) |
May
(11) |
Jun
(7) |
Jul
(13) |
Aug
(39) |
Sep
(58) |
Oct
(20) |
Nov
(63) |
Dec
(46) |
2003 |
Jan
|
Feb
|
Mar
(8) |
Apr
(52) |
May
(21) |
Jun
(2) |
Jul
(10) |
Aug
|
Sep
(6) |
Oct
(1) |
Nov
(1) |
Dec
(1) |
2004 |
Jan
|
Feb
(2) |
Mar
|
Apr
(1) |
May
(5) |
Jun
(46) |
Jul
(15) |
Aug
(1) |
Sep
(12) |
Oct
(3) |
Nov
(4) |
Dec
|
2005 |
Jan
|
Feb
(2) |
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
(6) |
Sep
|
Oct
|
Nov
|
Dec
(2) |
2006 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
(5) |
Aug
(2) |
Sep
(2) |
Oct
(3) |
Nov
(7) |
Dec
(2) |
2007 |
Jan
(8) |
Feb
(16) |
Mar
(17) |
Apr
(16) |
May
(21) |
Jun
(17) |
Jul
(40) |
Aug
(62) |
Sep
(30) |
Oct
(14) |
Nov
(7) |
Dec
(9) |
2008 |
Jan
(4) |
Feb
(7) |
Mar
(36) |
Apr
(22) |
May
(21) |
Jun
(9) |
Jul
(35) |
Aug
(17) |
Sep
(21) |
Oct
(24) |
Nov
(61) |
Dec
(85) |
2009 |
Jan
(51) |
Feb
(36) |
Mar
(60) |
Apr
(77) |
May
(154) |
Jun
(118) |
Jul
(86) |
Aug
(30) |
Sep
(20) |
Oct
(31) |
Nov
(10) |
Dec
(25) |
2010 |
Jan
(15) |
Feb
(17) |
Mar
(38) |
Apr
(59) |
May
(84) |
Jun
(63) |
Jul
(39) |
Aug
(43) |
Sep
(12) |
Oct
(6) |
Nov
(2) |
Dec
(2) |
2011 |
Jan
(2) |
Feb
|
Mar
(3) |
Apr
(1) |
May
|
Jun
(3) |
Jul
(2) |
Aug
(1) |
Sep
(3) |
Oct
(1) |
Nov
(4) |
Dec
(1) |
2012 |
Jan
(3) |
Feb
(1) |
Mar
(4) |
Apr
|
May
(1) |
Jun
(3) |
Jul
(1) |
Aug
(2) |
Sep
(3) |
Oct
(1) |
Nov
(1) |
Dec
(3) |
2013 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
(4) |
Sep
(7) |
Oct
(8) |
Nov
(1) |
Dec
(9) |
2014 |
Jan
(8) |
Feb
(4) |
Mar
(3) |
Apr
(3) |
May
(7) |
Jun
(2) |
Jul
(5) |
Aug
(5) |
Sep
(3) |
Oct
(11) |
Nov
(5) |
Dec
(6) |
2015 |
Jan
(2) |
Feb
(2) |
Mar
(2) |
Apr
(5) |
May
(3) |
Jun
|
Jul
(4) |
Aug
|
Sep
|
Oct
(1) |
Nov
(1) |
Dec
(1) |
2016 |
Jan
(1) |
Feb
|
Mar
(4) |
Apr
(3) |
May
(7) |
Jun
(2) |
Jul
(1) |
Aug
(3) |
Sep
(1) |
Oct
(1) |
Nov
(1) |
Dec
(3) |
2017 |
Jan
|
Feb
(1) |
Mar
(2) |
Apr
(3) |
May
(2) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Adrian M. <ad...@mc...> - 2002-02-23 01:00:30
|
I was asked to contribute a HOWTO type document on this... here is my first effort: TASKLETS IN THE PURU PURU DRIVER Notes for developers Copyright, Adrian McMenamin, 2002 ad...@mc... The author's moral rights have been asserted. 0 THE HARDWARE 0.1 PURU PURU Puru Puru is Sega's name for their rumble pack. This pack can be attached to a Dreamcast controller and when activated will cause the controller to shake (rumble). 0.2 MAPLE Maple is Sega's name for their USB-like bus. Devices are chained together and connected to the DC through the four connection ports. Keyboards, mouses, controllers, rumble packs and microphones are all Maple devices. For more details on this please see: http://mc.pp.se/dc/maplebus.html 1 THE DRIVER 1.1 FF Interface There is a Linux 'force feedback' interface. The rumble pack is a force feedback device. The interface is currently (February 2002) in rapid transition and it's best to check the latest kernel documentation at: linux/Documentation/input/ff.txt 1.1.1 Storing an effect The user application can store a standard effect in the driver memory space (eg a 2 second rumble for an explosion). Once stored such an effect is (in theory) available to all running applications on the DC. Code that does this: static int puru_ioctl(struct inode *inode, struct file *filip, unsigned int cmd, unsigned long arg) { ..... case EVIOCSFF: ..... ff_upload = kmalloc(sizeof(struct ff_effect), GFP_KERNEL); if (ff_upload == NULL) return -ENOMEM; copy_from_user(ff_upload, (void *) arg, sizeof(struct ff_effect)); /* Now get the length */ ff_time = ff_upload->replay; new_effect = kmalloc(sizeof(struct dc_puru_effect), GFP_KERNEL); if (new_effect == NULL) { kfree(ff_upload); return -ENOMEM; } new_effect->length_of_time = ff_time.length; new_effect->sid = effect_number++; list_add_tail(&new_effect->list, &effects_list); /* report the effect number back to the caller */ ff_upload->id = new_effect->sid; copy_to_user(arg, ff_upload, sizeof(struct ff_effect)); kfree(ff_upload); return 0; 1.1.2 Playing an effect The user application plays an effect by opening the relevant device file (eg /dev/input/event3) and 'writing' an input event to the file... static ssize_t puru_play(struct file *file, const char *buffer, size_t count, loff_t * pos) { ........ struct input_event *play = kmalloc(sizeof(struct input_event), GFP_KERNEL); ........ copy_from_user(play, buffer, sizeof(struct input_event)); ........ u16 find_id = play->code; struct dc_puru_effect *entry; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { play_puru(puru); /* Set up the timing measurements */ atomic_set(&puru->active, 1); puru->timeleft = entry->length_of_time; kfree(play); return 0; } } 1,2 Timing the effect The driver needs to time the length of the rumble without blocking - ie the DC needs to get on with other things and not just wait for the clock to run down the required length of time. It does this by hooking the vblank interrupt - called 60 times a second to enable a display update. 1.2.1 Hooking the interrupt This is actually done deep down in the maple driver mecahnism itself. But the Puru Puru driver registers that it wishes to be called by the maple interrupt handler - along with the other maple devices also hooked up. 1.2.2 The interrupt handler This is this code... void dc_purupuru_wake(struct maple_driver_data *data) { if (last_jiffies == -1) { last_jiffies = jiffies; return; } tasklet_schedule(&switchoff_tasklet); } The first bit only operates once in the driver's history and initialises a variable used to determine elapsed time. The second bit schedules a tasklet to run. 2. TASKLETS 2.1 URL to check http://www.xml.com/ldd/chapter/book/ This will tell you far more than I can! 2.2 What's a tasklet then? As the book says... Tasklets and Bottom-Half Processing "One of the main problems with interrupt handling is how to perform longish tasks within a handler. Often a substantial amount of work must be done in response to a device interrupt, but interrupt handlers need to finish up quickly and not keep interrupts blocked for long. These two needs (work and speed) conflict with each other, leaving the driver writer in a bit of a bind. "Linux (along with many other systems) resolves this problem by splitting the interrupt handler into two halves. The so-called top half is the routine that actually responds to the interrupt -- the one you register with request_irq. The bottom half is a routine that is scheduled by the top half to be executed later, at a safer time. ..... "Tasklets were introduced late in the 2.3 development series; they are now the preferred way to do bottom-half processing," (This and subsequent excerpts from "Linux Device Drivers" by Alassandro Rubini and Jonathan Corbet, published by O'Reilly) 2.3 Declaring a tasklet This is quite simple ... again, from the book: "Software support for tasklets is part of <linux/interrupt.h>, and the tasklet itself must be declared with one of the following: "DECLARE_TASKLET(name, function, data); "Declares a tasklet with the given name; when the tasklet is to be executed (as described later), the given function is called with the (unsigned long) data value. "DECLARE_TASKLET_DISABLED(name, function, data); "Declares a tasklet as before, but its initial state is "disabled,'' meaning that it can be scheduled but will not be executed until enabled at some future time. ..... "When your driver wants to schedule a tasklet to run, it calls tasklet_schedule: "tasklet_schedule(&jiq_tasklet);" 3. TASKLETS IN PURU PURU 3.1 Declaration DECLARE_TASKLET(switchoff_tasklet, switchoff_puru, (unsigned long) 0); 3.2 Tasklet void switchoff_puru(unsigned long x) { int gap; if (time_before(jiffies, last_jiffies)) { gap = ((jiffies + (0xffffffff - last_jiffies)) * 1000) / HZ; } else gap = ((jiffies - last_jiffies) * 1000) / HZ; last_jiffies = jiffies; /* which Puru Puru packs are active? */ struct dc_purupuru *select_me; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (atomic_read(&select_me->active) == 1) { select_me->timeleft -= gap; if (select_me->timeleft <= 0) { atomic_set(&select_me->active, 0); halt_puru(select_me); } } } return; } This is scheduled by the vblank routine shown above...(this counts down the milliseconds left before the rumble pack effect has used it up it's allotted time). 3.3 Enabling and disabling the tasklet The tasklet should not be scheduled when no puru puru device is installed - so this code disbales the tasklet as soon as the driver is initialised: static int __init dc_purupuru_init(void) { maple_register_driver(&dc_purupuru_driver); printk("Puru Puru Pack driver for Dreamcast Linux version 0.1\n"); tasklet_disable(&switchoff_tasklet); return 0; } The tasklet is then enabled once a puru pack has been plugged in: static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev) { struct input_handle *handle; /* Is this a Puru Puru device? */ if (dev->open != dc_puru_open) return NULL; if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; input_open_device(handle); printk(KERN_INFO "purupuru.c: adding Puru Puru Pack input%d\n", dev->number); tasklet_enable(&switchoff_tasklet); handler->minor = PURU_MINOR_BASE + dev->number; return handle; } ============================================================= |
From: Adrian M. <ad...@mc...> - 2002-02-22 01:34:09
|
Those who are subscribed to the cvs list will now know I've posted an update to the Puru Puru (rumble) Pack driver - the change does not affect functionality in any way, but (in theory) should make for better use of the DC's resources. A unified patch is below.... =================================================================== RCS file: /cvsroot/linuxdc/linux-sh-dc/drivers/char/joystick/purupuru.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- linuxdc/linux-sh-dc/drivers/char/joystick/purupuru.c 2002/02/21 01:48:06 1.1 +++ linuxdc/linux-sh-dc/drivers/char/joystick/purupuru.c 2002/02/22 01:26:23 1.2 @@ -32,6 +32,7 @@ #include <asm/uaccess.h> #include <linux/mm.h> #include <asm/atomic.h> +#include <linux/interrupt.h> #define PURU_MINOR_BASE 64 #define PPP_VERSION 1 @@ -333,13 +334,9 @@ return -EINVAL; } -void dc_purupuru_wake(struct maple_driver_data *data) +/* vblank interrupt handling */ +void switchoff_puru(unsigned long x) { - if (last_jiffies == -1) { - last_jiffies = jiffies; - return; - } - int gap; if (time_before(jiffies, last_jiffies)) { gap = @@ -367,6 +364,20 @@ return; } +DECLARE_TASKLET(switchoff_tasklet, switchoff_puru, (unsigned long) 0); + +void dc_purupuru_wake(struct maple_driver_data *data) +{ + if (last_jiffies == -1) { + last_jiffies = jiffies; + return; + } + + tasklet_schedule(&switchoff_tasklet); + + +} + /* * Link the maple, input and fops @@ -409,6 +420,7 @@ printk(KERN_INFO "purupuru.c: adding Puru Puru Pack input%d\n", dev->number); + tasklet_enable(&switchoff_tasklet); handler->minor = PURU_MINOR_BASE + dev->number; return handle; @@ -435,6 +447,7 @@ maple_register_driver(&dc_purupuru_driver); printk("Puru Puru Pack driver for Dreamcast Linux version 0.1\n"); + tasklet_disable(&switchoff_tasklet); return 0; } |
From: Adrian M. <ad...@mc...> - 2002-02-21 02:10:05
|
Just added this to CVS but have already noticed this bug.... To an extent Maple devices that are not plugged directly into the DC (eg Puru Puru, Microphone, VMU) are meant to be hot pluggable. But the Puru Puru it can only be plugged in twice, on the third time the input system get stuck in an endless loop and - if sound performance is any guide - the whole SH4 is stuffed (ie no other process gets a look in). Not the most deadly of bugs but one you should all be aware of. Adrian |
From: M. R. B. <mr...@0x...> - 2002-02-20 20:25:58
|
* Adrian McMenamin <ad...@mc...> on Wed, Feb 20, 2002: >=20 > I need to add it into the CVS and I propose to do that in=20 > /drivers/char/joystick as that is where the iforce driver (the only other= FF=20 > driver extant AFAICS) lives in the kernel tree. Make sense? >=20 Whenever in doubt about driver placement, put it where the rest of the similiar drivers live. > I am also aware I need to add a makefile and a menuconfiguration file -= =20 > though I have to confess I don't know quite how to go about this :-<. Rea= ding=20 > up on it now.... >=20 Actually, you need to add *to* the Makefile and to the Config.in file, you don't have to create them from scratch, it's easier than you think. The appropiate Makefile and Config.in should already exist in drivers/char/joystick/. M. R. |
From: Adrian M. <ad...@mc...> - 2002-02-20 20:05:47
|
On Tuesday 19 Feb 2002 12:27 am, Adrian McMenamin wrote: > With further thanks to Paul... > > > /* > * Purupuru driver > * for SEGA Dreamcast > * Copyright (c) Adrian McMenamin, 2002 Okay, well nobody has come back to me on this in the last 42 hours or so, so I assume there are no obvious problems (but have any of you actually tested it?). I need to add it into the CVS and I propose to do that in /drivers/char/joystick as that is where the iforce driver (the only other FF driver extant AFAICS) lives in the kernel tree. Make sense? I am also aware I need to add a makefile and a menuconfiguration file - though I have to confess I don't know quite how to go about this :-<. Reading up on it now.... Adrian |
From: Adrian M. <ad...@mc...> - 2002-02-19 00:27:13
|
With further thanks to Paul... /* * Purupuru driver * for SEGA Dreamcast * Copyright (c) Adrian McMenamin, 2002 * * Licenced under the terms * of the Free Software Foundation * General Public Licence * Version 2 * See http://www.gnu.org * * Based on pre-existing Maple device coding * Copyright MR Brown, YAEGASHI Takeshi and others * * */ /* * First posted 18 Feburary 2002 * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/maple.h> #include <linux/list.h> #include <asm/uaccess.h> #include <linux/mm.h> #include <asm/atomic.h> #define PURU_MINOR_BASE 64 #define PPP_VERSION 1 #define _DEBUG_ #ifdef _DEBUG_ #define DEBGM(fmt, args...) (printk(KERN_ERR fmt, ##args)) #else #define DEBGM(fmt, args...) ((void) 0) #endif #define CHECKFUNC EVIOCGBIT(EV_FF, 16) static int last_minor; static unsigned long last_jiffies = -1; /* one list shared through all devices */ LIST_HEAD(effects_list); /* may be more than one puru puru attached */ LIST_HEAD(devices_list); struct dc_puru_effect { int length_of_time; u16 sid; struct list_head list; }; struct dc_purupuru { struct input_dev dev; struct maple_driver_data *data; struct list_head list; int minor; atomic_t active; int timeleft; }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev); static struct input_handler purupuru_handler; static int dc_puru_open(struct input_dev *dev) { return 0; } static void dc_puru_close(struct input_dev *dev) { } static int dc_purupuru_connect(struct maple_driver_data *d) { /*unsigned long data = d->function_data; */ struct dc_purupuru *puru; if (!(puru = kmalloc(sizeof(struct dc_purupuru), GFP_KERNEL))) { DEBGM("Memory allocation failure\n"); return -ENOMEM; } memset(puru, 0, sizeof(struct dc_purupuru)); puru->data = d; d->private_data = puru; puru->dev.private = puru; puru->dev.open = dc_puru_open; puru->dev.close = dc_puru_close; puru->dev.name = d->dev->product_name; puru->dev.event = NULL; puru->dev.idbus = BUS_MAPLE; input_register_device(&puru->dev); input_register_handler(&purupuru_handler); puru->minor = ((puru->dev.handle)->handler)->minor; /* Add this device to the list */ list_add_tail(&puru->list, &devices_list); return 0; } static void dc_purupuru_disconnect(struct maple_driver_data *d) { struct dc_purupuru *puru = d->private_data; input_unregister_device(&puru->dev); /* Remove this device from list */ list_del(&puru->list); kfree(puru); } /* static void dc_purupuru_callback(struct maple_driver_data *data) */ /* { */ /* struct mapleq *mq = &data->mq; */ /* int res = mq->recvbuf[0]; */ /* printk */ /* ("Maple reply (%d, %d) cmd=%d => %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", */ /* mq->port, mq->unit, mq->command, res, mq->recvbuf[1], */ /* mq->recvbuf[2], mq->recvbuf[3], mq->recvbuf[4], */ /* mq->recvbuf[5], mq->recvbuf[6], mq->recvbuf[7], */ /* mq->recvbuf[8], mq->recvbuf[9], mq->recvbuf[10], */ /* mq->recvbuf[11]); */ /* } */ static int effect_number = 0; static int puru_ioctl(struct inode *inode, struct file *filip, unsigned int cmd, unsigned long arg) { struct ff_effect *ff_upload; struct ff_replay ff_time; struct dc_puru_effect *new_effect, *entry; struct list_head *ptr; switch (cmd) { case EVIOCGVERSION: return put_user(PPP_VERSION, (int *) arg); case CHECKFUNC: return put_user(FF_RUMBLE | FF_CONSTANT, (int *) arg); case EVIOCGEFFECTS: return put_user(1, (int *) arg); /* Save a FF effect */ case EVIOCSFF: /* * Currently * only supports: * length * */ ff_upload = kmalloc(sizeof(struct ff_effect), GFP_KERNEL); if (ff_upload == NULL) return -ENOMEM; copy_from_user(ff_upload, (void *) arg, sizeof(struct ff_effect)); /* Now get the length */ ff_time = ff_upload->replay; new_effect = kmalloc(sizeof(struct dc_puru_effect), GFP_KERNEL); if (new_effect == NULL) { kfree(ff_upload); return -ENOMEM; } new_effect->length_of_time = ff_time.length; new_effect->sid = effect_number++; list_add_tail(&new_effect->list, &effects_list); /* report the effect number back to the caller */ ff_upload->id = new_effect->sid; copy_to_user(arg, ff_upload, sizeof(struct ff_effect)); kfree(ff_upload); return 0; case EVIOCRMFF: if (list_empty(&effects_list)) return -EINVAL; /* walk through the list */ u16 find_id = (u16) arg; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { list_del(&entry->list); kfree(entry); return 0; } } return -EINVAL; default: return -1; } } static int puru_open(struct inode *inode, struct file *file) { file->private_data = MINOR(inode->i_rdev); return 0; } static void halt_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *halt = &(puru->data->mq); halt->command = 14; halt->length = 2; ((unsigned long *) (halt->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (halt->recvbuf))[1] = 0x00; halt->sendbuf = halt->recvbuf; if (maple_add_packet(halt) != 0) DEBGM("Could not add packet\n"); } static void play_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *play = &(puru->data->mq); play->command = 14; play->length = 2; ((unsigned long *) (play->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (play->recvbuf))[1] = 0x0000ffff; play->sendbuf = play->recvbuf; if (maple_add_packet(play) != 0) DEBGM("Could not add packet\n"); } /* 'Write' routine to play effects */ static ssize_t puru_play(struct file *file, const char *buffer, size_t count, loff_t * pos) { last_minor = file->private_data; /* buffer is a pointer to an input event */ struct input_event *play = kmalloc(sizeof(struct input_event), GFP_KERNEL); if (play == NULL) return -ENOMEM; memset(play, 0, sizeof(struct input_event)); copy_from_user(play, buffer, sizeof(struct input_event)); if (play->type != EV_FF) { kfree(play); return -EINVAL; } /* Which purupuru device are we playing with? */ struct dc_purupuru *select_me, *puru = NULL; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (select_me->minor == last_minor) { puru = select_me; break; } } if (puru == NULL) { kfree(play); return -ENODEV; } /* Stop? In which case halt the Puru Puru immediately */ if (play->value == 0) { halt_puru(puru); kfree(play); return 0; } /* Walk through the list looking for the desired effect */ u16 find_id = play->code; struct dc_puru_effect *entry; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { play_puru(puru); /* Set up the timing measurements */ atomic_set(&puru->active, 1); puru->timeleft = entry->length_of_time; kfree(play); return 0; } } kfree(play); return -EINVAL; } void dc_purupuru_wake(struct maple_driver_data *data) { if (last_jiffies == -1) { last_jiffies = jiffies; return; } int gap; if (time_before(jiffies, last_jiffies)) { gap = ((jiffies + (0xffffffff - last_jiffies)) * 1000) / HZ; } else gap = ((jiffies - last_jiffies) * 1000) / HZ; last_jiffies = jiffies; /* which Puru Puru packs are active? */ struct dc_purupuru *select_me; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (atomic_read(&select_me->active) == 1) { select_me->timeleft -= gap; if (select_me->timeleft <= 0) { atomic_set(&select_me->active, 0); halt_puru(select_me); } } } return; } /* * Link the maple, input and fops * functions of this driver */ static struct maple_driver dc_purupuru_driver = { function:MAPLE_FUNC_PURUPURU, name:"Puru Puru Pack", connect:dc_purupuru_connect, disconnect:dc_purupuru_disconnect, /* reply:dc_purupuru_callback, */ vblank:dc_purupuru_wake, }; static struct file_operations puru_ops = { owner:THIS_MODULE, ioctl:puru_ioctl, write:puru_play, open:puru_open, }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev) { struct input_handle *handle; /* Is this a Puru Puru device? */ if (dev->open != dc_puru_open) return NULL; if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; input_open_device(handle); printk(KERN_INFO "purupuru.c: adding Puru Puru Pack input%d\n", dev->number); handler->minor = PURU_MINOR_BASE + dev->number; return handle; } static void purupuru_disconnect(struct input_handle *handle) { input_close_device(handle); kfree(handle); } static struct input_handler purupuru_handler = { connect:purupuru_connect, disconnect:purupuru_disconnect, fops:&puru_ops, minor:PURU_MINOR_BASE, }; static int __init dc_purupuru_init(void) { maple_register_driver(&dc_purupuru_driver); printk("Puru Puru Pack driver for Dreamcast Linux version 0.1\n"); return 0; } static void __exit dc_purupuru_exit(void) { struct list_head *ptr = NULL; struct list_head *nxt = NULL; struct dc_puru_effect *effect; maple_unregister_driver(&dc_purupuru_driver); input_unregister_handler(&purupuru_handler); if (!list_empty(&effects_list)) { list_for_each_safe(ptr, nxt, &effects_list) { effect = list_entry(ptr, struct dc_puru_effect, list); if (effect) { list_del(&(effect->list)); kfree(effect); } } } } module_init(dc_purupuru_init); module_exit(dc_purupuru_exit); MODULE_AUTHOR("Adrian McMenamin <ad...@mc...>"); MODULE_DESCRIPTION("SEGA Dreamcast purupuru driver"); |
From: Adrian M. <ad...@mc...> - 2002-02-18 22:25:48
|
With thanks to Paul Mundt.... dump the previous one and test with this instead... /* * Purupuru driver * for SEGA Dreamcast * Copyright (c) Adrian McMenamin, 2002 * * Licenced under the terms * of the Free Software Foundation * General Public Licence * Version 2 * See http://www.gnu.org * * Based on pre-existing Maple device coding * Copyright MR Brown, YAEGASHI Takeshi and others * * */ /* * First posted 18 Feburary 2002 * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/maple.h> #include <linux/list.h> #include <asm/uaccess.h> #include <linux/mm.h> #include <asm/atomic.h> #define PURU_MINOR_BASE 64 #define PPP_VERSION 1 #define _DEBUG_ #ifdef _DEBUG_ #define DEBGM(fmt, args...) (printk(KERN_ERR fmt, ##args)) #else #define DEBGM(fmt, args...) ((void) 0) #endif #define CHECKFUNC EVIOCGBIT(EV_FF, 16) static int last_minor; static unsigned long last_jiffies = -1; /* one list shared through all devices */ LIST_HEAD(effects_list); /* may be more than one puru puru attached */ LIST_HEAD(devices_list); struct dc_puru_effect { int length_of_time; u16 sid; struct list_head list; }; struct dc_purupuru { struct input_dev dev; struct maple_driver_data *data; struct list_head list; int minor; atomic_t active; int timeleft; }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev); static struct input_handler purupuru_handler; static int dc_puru_open(struct input_dev *dev) { return 0; } static void dc_puru_close(struct input_dev *dev) { } static int dc_purupuru_connect(struct maple_driver_data *d) { /*unsigned long data = d->function_data; */ struct dc_purupuru *puru; if (!(puru = kmalloc(sizeof(struct dc_purupuru), GFP_KERNEL))) { DEBGM("Memory allocation failure\n"); return -ENOMEM; } memset(puru, 0, sizeof(struct dc_purupuru)); puru->data = d; d->private_data = puru; puru->dev.private = puru; puru->dev.open = dc_puru_open; puru->dev.close = dc_puru_close; puru->dev.name = d->dev->product_name; puru->dev.event = NULL; puru->dev.idbus = BUS_MAPLE; input_register_device(&puru->dev); input_register_handler(&purupuru_handler); puru->minor = ((puru->dev.handle)->handler)->minor; /* Add this device to the list */ list_add_tail(&puru->list, &devices_list); return 0; } static void dc_purupuru_disconnect(struct maple_driver_data *d) { struct dc_purupuru *puru = d->private_data; input_unregister_device(&puru->dev); /* Remove this device from list */ list_del(&puru->list); kfree(puru); } /* static void dc_purupuru_callback(struct maple_driver_data *data) */ /* { */ /* struct mapleq *mq = &data->mq; */ /* int res = mq->recvbuf[0]; */ /* printk */ /* ("Maple reply (%d, %d) cmd=%d => %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", */ /* mq->port, mq->unit, mq->command, res, mq->recvbuf[1], */ /* mq->recvbuf[2], mq->recvbuf[3], mq->recvbuf[4], */ /* mq->recvbuf[5], mq->recvbuf[6], mq->recvbuf[7], */ /* mq->recvbuf[8], mq->recvbuf[9], mq->recvbuf[10], */ /* mq->recvbuf[11]); */ /* } */ static int effect_number = 0; static int puru_ioctl(struct inode *inode, struct file *filip, unsigned int cmd, unsigned long arg) { struct ff_effect *ff_upload; struct ff_replay ff_time; struct dc_puru_effect *new_effect, *entry; struct list_head *ptr; switch (cmd) { case EVIOCGVERSION: return put_user(PPP_VERSION, (int *) arg); case CHECKFUNC: return put_user(FF_RUMBLE | FF_CONSTANT, (int *) arg); case EVIOCGEFFECTS: return put_user(1, (int *) arg); /* Save a FF effect */ case EVIOCSFF: /* * Currently * only supports: * length * */ ff_upload = kmalloc(sizeof(struct ff_effect), GFP_KERNEL); if (ff_upload == NULL) return -ENOMEM; copy_from_user(ff_upload, (void *) arg, sizeof(struct ff_effect)); /* Now get the length */ ff_time = ff_upload->replay; new_effect = kmalloc(sizeof(struct dc_puru_effect), GFP_KERNEL); if (new_effect == NULL){ kfree(ff_upload); return -ENOMEM; } new_effect->length_of_time = ff_time.length; new_effect->sid = effect_number++; list_add_tail(&new_effect->list, &effects_list); /* report the effect number back to the caller */ ff_upload->id = new_effect->sid; copy_to_user(arg, ff_upload, sizeof(struct ff_effect)); kfree(ff_upload); return 0; case EVIOCRMFF: if (list_empty(&effects_list)) return -EINVAL; /* walk through the list */ u16 find_id = (u16) arg; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { list_del(&entry->list); kfree(entry); return 0; } } return -EINVAL; default: return -1; } } static int puru_open(struct inode *inode, struct file *file) { file->private_data = MINOR(inode->i_rdev); return 0; } static void halt_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *halt = &(puru->data->mq); halt->command = 14; halt->length = 2; ((unsigned long *) (halt->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (halt->recvbuf))[1] = 0x00; halt->sendbuf = halt->recvbuf; if (maple_add_packet(halt) != 0) DEBGM("Could not add packet\n"); } static void play_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *play = &(puru->data->mq); play->command = 14; play->length = 2; ((unsigned long *) (play->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (play->recvbuf))[1] = 0x0000ffff; play->sendbuf = play->recvbuf; if (maple_add_packet(play) != 0) DEBGM("Could not add packet\n"); } /* 'Write' routine to play effects */ static ssize_t puru_play(struct file *file, const char *buffer, size_t count, loff_t * pos) { last_minor = file->private_data; /* buffer is a pointer to an input event */ struct input_event *play = kmalloc(sizeof(struct input_event), GFP_KERNEL); if (play == NULL) return -ENOMEM; memset(play, 0, sizeof(struct input_event)); copy_from_user(play, buffer, sizeof(struct input_event)); if (play->type != EV_FF){ kfree(play); return -EINVAL; } /* Which purupuru device are we playing with? */ struct dc_purupuru *select_me, *puru = NULL; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (select_me->minor == last_minor) { puru = select_me; break; } } if (puru == NULL){ kfree(play); return -ENODEV; } /* Stop? In which case halt the Puru Puru immediately */ if (play->value == 0) { halt_puru(puru); kfree(play); return 0; } /* Walk through the list looking for the desired effect */ u16 find_id = play->code; struct dc_puru_effect *entry; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { play_puru(puru); /* Set up the timing measurements */ atomic_set(&puru->active, 1); puru->timeleft = entry->length_of_time; kfree(play); return 0; } } kfree(play); return -EINVAL; } void dc_purupuru_wake(struct maple_driver_data *data) { if (last_jiffies == -1) { last_jiffies = jiffies; return; } int gap; if (jiffies < last_jiffies) { gap = ((jiffies + (0xffff - last_jiffies)) * 1000) / HZ; } else gap = ((jiffies - last_jiffies) * 1000) / HZ; last_jiffies = jiffies; /* which Puru Puru packs are active? */ struct dc_purupuru *select_me; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (atomic_read(&select_me->active) == 1) { select_me->timeleft -= gap; if (select_me->timeleft <= 0) { atomic_set(&select_me->active, 0); halt_puru(select_me); } } } return; } /* * Link the maple, input and fops * functions of this driver */ static struct maple_driver dc_purupuru_driver = { function:MAPLE_FUNC_PURUPURU, name:"Puru Puru Pack", connect:dc_purupuru_connect, disconnect:dc_purupuru_disconnect, /* reply:dc_purupuru_callback, */ vblank:dc_purupuru_wake, }; static struct file_operations puru_ops = { owner:THIS_MODULE, ioctl:puru_ioctl, write:puru_play, open:puru_open, }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev) { struct input_handle *handle; /* Is this a Puru Puru device? */ if (dev->open != dc_puru_open) return NULL; if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; input_open_device(handle); printk(KERN_INFO "purupuru.c: adding Puru Puru Pack input%d\n", dev->number); handler->minor = PURU_MINOR_BASE + dev->number; return handle; } static void purupuru_disconnect(struct input_handle *handle) { input_close_device(handle); kfree(handle); } static struct input_handler purupuru_handler = { connect:purupuru_connect, disconnect:purupuru_disconnect, fops:&puru_ops, minor:PURU_MINOR_BASE, }; static int __init dc_purupuru_init(void) { maple_register_driver(&dc_purupuru_driver); printk("Puru Puru Pack driver for Dreamcast Linux version 0.1\n"); return 0; } static void __exit dc_purupuru_exit(void) { struct list_head *ptr = NULL; struct list_head *nxt = NULL; struct dc_puru_effect *effect; maple_unregister_driver(&dc_purupuru_driver); input_unregister_handler(&purupuru_handler); if (!list_empty(&effects_list)) { list_for_each_safe(ptr, nxt, &effects_list) { effect = list_entry(ptr, struct dc_puru_effect, list); if (effect) { list_del(&(effect->list)); kfree(effect); } } } } module_init(dc_purupuru_init); module_exit(dc_purupuru_exit); MODULE_AUTHOR("Adrian McMenamin <ad...@mc...>"); MODULE_DESCRIPTION("SEGA Dreamcast purupuru driver"); |
From: Adrian M. <ad...@mc...> - 2002-02-18 21:21:54
|
Well, here it is.... for testing /* * Purupuru driver * for SEGA Dreamcast * Copyright (c) Adrian McMenamin, 2002 * * Licenced under the terms * of the Free Software Foundation * General Public Licence * Version 2 * See http://www.gnu.org * * Based on pre-existing Maple device coding * Copyright MR Brown, YAEGASHI Takeshi and others * * */ /* * First posted 18 Feburary 2002 * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <linux/timer.h> #include <linux/maple.h> #include <linux/list.h> #include <asm/uaccess.h> #include <linux/mm.h> #define PURU_MINOR_BASE 64 #define PPP_VERSION 1 #define _DEBUG_ #ifdef _DEBUG_ #define DEBGM(fmt, args...) (printk(KERN_ERR fmt, ##args)) #else #define DEBGM(fmt, args...) ((void) 0) #endif #define CHECKFUNC EVIOCGBIT(EV_FF, 16) int last_minor; int last_jiffies = -1; /* one list shared through all devices */ LIST_HEAD(effects_list); /* may be more than one puru puru attached */ LIST_HEAD(devices_list); struct dc_puru_effect { int length_of_time; u16 sid; struct list_head list; }; struct dc_purupuru { struct input_dev dev; struct maple_driver_data *data; struct list_head list; int minor; int active; int timeleft; }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev); static struct input_handler purupuru_handler; static int dc_puru_open(struct input_dev *dev) { return 0; } static void dc_puru_close(struct input_dev *dev) { } static int dc_purupuru_connect(struct maple_driver_data *d) { /*unsigned long data = d->function_data; */ struct dc_purupuru *puru; if (!(puru = kmalloc(sizeof(struct dc_purupuru), GFP_KERNEL))) { DEBGM("Memory allocation failure\n"); return -ENOMEM; } memset(puru, 0, sizeof(struct dc_purupuru)); puru->data = d; d->private_data = puru; puru->dev.private = puru; puru->dev.open = dc_puru_open; puru->dev.close = dc_puru_close; puru->dev.name = d->dev->product_name; puru->dev.event = NULL; puru->dev.idbus = BUS_MAPLE; input_register_device(&puru->dev); input_register_handler(&purupuru_handler); puru->minor = ((puru->dev.handle)->handler)->minor; /* Add this device to the list */ list_add_tail(&puru->list, &devices_list); return 0; } static void dc_purupuru_disconnect(struct maple_driver_data *d) { struct dc_purupuru *puru = d->private_data; input_unregister_device(&puru->dev); /* Remove this device from list */ list_del(&puru->list); kfree(puru); } /* static void dc_purupuru_callback(struct maple_driver_data *data) */ /* { */ /* struct mapleq *mq = &data->mq; */ /* int res = mq->recvbuf[0]; */ /* printk */ /* ("Maple reply (%d, %d) cmd=%d => %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", */ /* mq->port, mq->unit, mq->command, res, mq->recvbuf[1], */ /* mq->recvbuf[2], mq->recvbuf[3], mq->recvbuf[4], */ /* mq->recvbuf[5], mq->recvbuf[6], mq->recvbuf[7], */ /* mq->recvbuf[8], mq->recvbuf[9], mq->recvbuf[10], */ /* mq->recvbuf[11]); */ /* } */ static int effect_number = 0; static int puru_ioctl(struct inode *inode, struct file *filip, unsigned int cmd, unsigned long arg) { struct ff_effect *ff_upload; struct ff_replay ff_time; struct dc_puru_effect *new_effect, *entry; struct list_head *ptr; switch (cmd) { case EVIOCGVERSION: return put_user(PPP_VERSION, (int *) arg); case CHECKFUNC: return put_user(FF_RUMBLE | FF_CONSTANT, (int *) arg); case EVIOCGEFFECTS: return put_user(1, (int *) arg); /* Save a FF effect */ case EVIOCSFF: /* * Currently * only supports: * length * */ ff_upload = kmalloc(sizeof(struct ff_effect), GFP_KERNEL); copy_from_user(ff_upload, (void *) arg, sizeof(struct ff_effect)); /* Now get the length */ ff_time = ff_upload->replay; new_effect = kmalloc(sizeof(struct dc_puru_effect), GFP_KERNEL); if (new_effect == NULL) return -ENOMEM; new_effect->length_of_time = ff_time.length; new_effect->sid = effect_number++; list_add_tail(&new_effect->list, &effects_list); /* report the effect number back to the caller */ ff_upload->id = new_effect->sid; copy_to_user(arg, ff_upload, sizeof(struct ff_effect)); kfree(ff_upload); return 0; case EVIOCRMFF: if (list_empty(&effects_list)) return -EINVAL; /* walk through the list */ u16 find_id = (u16) arg; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { list_del(&entry->list); kfree(entry); return 0; } } return -EINVAL; default: return -1; } } static int puru_open(struct inode *inode, struct file *file) { file->private_data = MINOR(inode->i_rdev); return 0; } static void halt_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *halt = &(puru->data->mq); halt->command = 14; halt->length = 2; ((unsigned long *) (halt->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (halt->recvbuf))[1] = 0x00; halt->sendbuf = halt->recvbuf; if (maple_add_packet(halt) != 0) DEBGM("Could not add packet\n"); } static void play_puru(struct dc_purupuru *puru) { /* Halt the puru device immediately */ struct mapleq *halt = &(puru->data->mq); halt->command = 14; halt->length = 2; ((unsigned long *) (halt->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (halt->recvbuf))[1] = 0x0000ffff; halt->sendbuf = halt->recvbuf; if (maple_add_packet(halt) != 0) DEBGM("Could not add packet\n"); } /* 'Write' routine to play effects */ static ssize_t puru_play(struct file *file, const char *buffer, size_t count, loff_t * pos) { last_minor = file->private_data; /* buffer is a pointer to an input event */ struct input_event *play = kmalloc(sizeof(struct input_event), GFP_KERNEL); memset(play, 0, sizeof(struct input_event)); copy_from_user(play, buffer, sizeof(struct input_event)); if (play->type != EV_FF) return -EINVAL; /* Which purupuru device are we playing with? */ struct dc_purupuru *select_me, *puru = NULL; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (select_me->minor == last_minor) { puru = select_me; break; } } if (puru == NULL) return -ENODEV; /* Stop? In which case halt the Puru Puru immediately */ if (play->value == 0) { halt_puru(puru); return 0; } /* Walk through the list looking for the desired effect */ u16 find_id = play->code; struct dc_puru_effect *entry; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { play_puru(puru); /* Set up the timing measurements */ puru->active = 1; puru->timeleft = entry->length_of_time; return 0; } } return -EINVAL; } void dc_purupuru_wake(struct maple_driver_data *data) { if (last_jiffies == -1) { last_jiffies = jiffies; return; } int gap; if (jiffies < last_jiffies) { gap = ((jiffies + (0xffff - last_jiffies)) * 1000) / HZ; } else gap = ((jiffies - last_jiffies) * 1000) / HZ; last_jiffies = jiffies; /* which Puru Puru packs are active? */ struct dc_purupuru *select_me; struct list_head *ptr; for (ptr = devices_list.next; ptr != &devices_list; ptr = ptr->next) { select_me = list_entry(ptr, struct dc_purupuru, list); if (select_me->active == 1) { select_me->timeleft -= gap; if (select_me->timeleft <= 0) { select_me->active = 0; halt_puru(select_me); } } } return; } /* * Link the maple, input and fops * functions of this driver */ static struct maple_driver dc_purupuru_driver = { function:MAPLE_FUNC_PURUPURU, name:"Puru Puru Pack", connect:dc_purupuru_connect, disconnect:dc_purupuru_disconnect, /* reply:dc_purupuru_callback, */ vblank:dc_purupuru_wake, }; static struct file_operations puru_ops = { owner:THIS_MODULE, ioctl:puru_ioctl, write:puru_play, open:puru_open, }; static struct input_handle *purupuru_connect(struct input_handler *handler, struct input_dev *dev) { struct input_handle *handle; /* Is this a Puru Puru device? */ if (dev->open != dc_puru_open) return NULL; if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; input_open_device(handle); printk(KERN_INFO "purupuru.c: adding Puru Puru Pack input%d\n", dev->number); handler->minor = PURU_MINOR_BASE + dev->number; return handle; } static void purupuru_disconnect(struct input_handle *handle) { input_close_device(handle); kfree(handle); } static struct input_handler purupuru_handler = { connect:purupuru_connect, disconnect:purupuru_disconnect, fops:&puru_ops, minor:PURU_MINOR_BASE, }; static int __init dc_purupuru_init(void) { maple_register_driver(&dc_purupuru_driver); printk("Puru Puru Pack driver for Dreamcast Linux version 0.1\n"); return 0; } static void __exit dc_purupuru_exit(void) { struct list_head *ptr = NULL; struct list_head *nxt = NULL; struct dc_puru_effect *effect; maple_unregister_driver(&dc_purupuru_driver); input_unregister_handler(&purupuru_handler); if (!list_empty(&effects_list)) { list_for_each_safe(ptr, nxt, &effects_list) { effect = list_entry(ptr, struct dc_puru_effect, list); if (effect) { list_del(&(effect->list)); kfree(effect); } } } } module_init(dc_purupuru_init); module_exit(dc_purupuru_exit); MODULE_AUTHOR("Adrian McMenamin <ad...@mc...>"); MODULE_DESCRIPTION("SEGA Dreamcast purupuru driver"); |
From: Adrian M. <ad...@mc...> - 2002-02-18 02:05:10
|
This is now on the verge of completion, but I really need to test it with somebody who has two controllers - both with rumble packs! Anybody fit the bill? Thanks Adrian |
From: Adrian M. <ad...@mc...> - 2002-02-18 00:45:16
|
On Sunday 17 Feb 2002 11:54 pm, Adrian McMenamin wrote: > I have some code in my Puru Puru driver which does this: Worked out this one myself - the driver is getting there! |
From: Adrian M. <ad...@mc...> - 2002-02-17 23:54:31
|
I have some code in my Puru Puru driver which does this: u16 find_id = play->code; struct dc_puru_effect *entry; for (ptr = effects_list.next; ptr != &effects_list; ptr = ptr->next) { entry = list_entry(ptr, struct dc_puru_effect, list); if (entry->sid == find_id) { struct mapleq *rumble = maple_allocq(puru->data->dev); rumble->command = 14; rumble->length = 2; ((unsigned long *) (rumble->recvbuf))[0] = cpu_to_be32(MAPLE_FUNC_PURUPURU); ((unsigned long *) (rumble->recvbuf))[1] = 0x0000ffff; rumble->sendbuf = rumble->recvbuf; if (maple_add_packet(rumble) != 0) printk("Could not add packet\n"); ie it uses the maple low level commands to create a mapleq* and then adds it to the list of packets to be processed. However - while all this code appears to execute correctly and the function returns from maple_add_packet, the system then freezes. Has anybody else used this low level maple interface (which is different from the other supported maple devices, but is esentially the method used below them in the main maple.c) in this way? Any ideas of what might be going wrong in my code? Adrian |
From: M. R. B. <mr...@0x...> - 2002-02-14 00:13:30
|
* Adrian McMenamin <ad...@mc...> on Wed, Feb 13, 2002: > Just to make a point - please have a look at >=20 > /drivers/input/keybdev.c is the main kernel sources and you will see that= the=20 > dreaded "C++ style comments" have indeed been accepted into the kernel. S= o=20 > there :-> >=20 Indeed, although the *two* places where they are used (as opposed to the entire file) are used for printing debugging messages, so they were probably left in the original patch/sync unchanged to facilitate that. Still, our development policy is not to use them. Something I learned in art class about "Form over function..." :P. Who was that? Buckminster Fuller? M. R. |
From: Adrian M. <ad...@mc...> - 2002-02-13 23:56:28
|
Just to make a point - please have a look at /drivers/input/keybdev.c is the main kernel sources and you will see that the dreaded "C++ style comments" have indeed been accepted into the kernel. So there :-> Adrian |
From: mag <ma...@ro...> - 2002-02-12 03:36:30
|
> Hmm, so can I safely assume that you're using the linux-sh-dc from > CVS? Yep. > > Another idea that crossed my mind, was that this could be caused by > me > > trying to use devfs, but not having the devfsd (yet). But I don't > think > > that that's the problem, because I am pretty sure it does not even > get > > to the first startup scripts anyway, and that's where it is normally > > started. > > Could you let us know if it still fails if you use the manually > created > device node (`mknod gdrom b 250 0')? I tried doing that and compiling devfs out of the kernel, but that did not do anything. > Also, I've attached a patch that prints out GD-ROM command values in > hex. > If the problem persists, could you send a dump of the command values > printed? > > If it's something more, I'll try to look into it. NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. gdrom_do_command: 00: 70 1f 00 00 00 00 gdrom_do_command: 06: 00 00 00 00 00 00 gdrom_do_command: 00: 30 20 00 00 96 00 gdrom_do_command: 06: 00 00 00 00 01 00 GD-ROM: Got larger result maple(1,0): Connected(function 0x1) input0: controller(0xf06fe): Dreamcast Controller maple(0,0): Connected(function 0x1) input1: controller(0xf06fe): Dreamcast Controller maple(0,1): Connected(function 0xe) maple(0,1): No driver for function(s): e. Any ideas on what's wrong here? Thanks, mag. |
From: Adrian M. <ad...@mc...> - 2002-02-10 23:59:43
|
Unfortunately the API for this is a little more complex (or powerful if you prefer) than I first thought, so there won't be any diver tonight. Will be in the next few days though. What I'd really like to do is get the microphone driver working.... but that remains a case of so near, yet so far. Adrian |
From: Adrian M. <ad...@mc...> - 2002-02-10 22:15:15
|
On Sunday 10 Feb 2002 9:59 pm, M. R. Brown wrote: > * RC5Stint <rc5...@ya...> on Sun, Feb 10, 2002: > > After all this research, just provide an API for this type of device, > > make it portable and generic enough, and leave it to UI and > > application developers to worry about what to do with it. Don't > > sweat it if you yourself can't find a *nix use for the device. > > Fortunately he's saved from such a trek, since this API does exist in 2.4 > already :P. > > > Take a break and shug a beer (or two, or three.) You've been > > thinking too hard. :-) > > Shoot, at the pace he's going he'll have all Maple devices accounted for in > the next few weeks! > That's setting me up for a fall - I only started on this one because I'd reached a deadend with the mic :-> > > Not trying to insult you, but if it only does output, it's obviously > > an output device, dude. > > /me comes to Adrian's defense... > > Technically yes, but in the conventional sense vibration units are usually > built into the joystick device. Sega's like the only group insane enough > to physically abstract that implementation, but it still belongs to the > input API, since it follows that general model. > I talked to the author of the kernel FF API and I think he was pretty surprised about the brain dead nature of Puru Puru (ie there seems to be little flexibility in terms of cpu->user and nothing from user->cpu. When you consider that there are people out their dealing with > M. R. |
From: Adrian M. <ad...@mc...> - 2002-02-10 22:15:12
|
On Sunday 10 Feb 2002 9:59 pm, M. R. Brown wrote: > * RC5Stint <rc5...@ya...> on Sun, Feb 10, 2002: > > After all this research, just provide an API for this type of device, > > make it portable and generic enough, and leave it to UI and > > application developers to worry about what to do with it. Don't > > sweat it if you yourself can't find a *nix use for the device. > > Fortunately he's saved from such a trek, since this API does exist in 2.4 > already :P. > > > Take a break and shug a beer (or two, or three.) You've been > > thinking too hard. :-) > > Shoot, at the pace he's going he'll have all Maple devices accounted for in > the next few weeks! > > > Not trying to insult you, but if it only does output, it's obviously > > an output device, dude. > > /me comes to Adrian's defense... > > Technically yes, but in the conventional sense vibration units are usually > built into the joystick device. Sega's like the only group insane enough > to physically abstract that implementation, but it still belongs to the > input API, since it follows that general model. > > M. R. Oops didn't finsh that last one... When you consider that there are people out their dealing with devices where feedback goes both ways having this as an input device makes perfect sense - its just a very limited example of a larger class of devices. Adrian |
From: M. R. B. <mr...@0x...> - 2002-02-10 22:01:33
|
* RC5Stint <rc5...@ya...> on Sun, Feb 10, 2002: >=20 > After all this research, just provide an API for this type of device, > make it portable and generic enough, and leave it to UI and > application developers to worry about what to do with it. Don't > sweat it if you yourself can't find a *nix use for the device. >=20 Fortunately he's saved from such a trek, since this API does exist in 2.4 already :P. >=20 > Take a break and shug a beer (or two, or three.) You've been > thinking too hard. :-) >=20 Shoot, at the pace he's going he'll have all Maple devices accounted for in the next few weeks! > Not trying to insult you, but if it only does output, it's obviously > an output device, dude. >=20 /me comes to Adrian's defense... Technically yes, but in the conventional sense vibration units are usually built into the joystick device. Sega's like the only group insane enough to physically abstract that implementation, but it still belongs to the input API, since it follows that general model. M. R. |
From: RC5Stint <rc5...@ya...> - 2002-02-10 21:14:33
|
> The thing is - I cannot find a standard API/IF for this sort > of thing for Unix/Linux. I could write a custom one, but what's > the point of that? If a custom API is generic enough and usable enough, it soon becomes a standard. Look what happened with IrixGL. :-) I seem to remember an article on force feedback devices from MIT Media Lab a few months ago, on an Assoc. of Computing Machinery magazine (most probably Communications of the ACM.) The article claimed force feedback can be used along with sound cues and speech synthesis, to successfully guide the blind when using a GUI'ed computer. The article referred to a mountain of previous Media Lab research on the subject, most of it on ACM and IEEE publications. > If the idea is to have games/software that is as portable > as possible then it doesn't make sense. Sure it does! To make the API portable and generic enough, take a look at force feedback APIs in other game console and PC platforms, or just examine the behavior of similar devices in other gaming platforms (N64 Rumble Pak, PS1 DualShock, PS2 DualShock 2, etc.) The PS2 Linux kit supports the Dual Shocks through some API. The source code to the kit is GPL'ed, and is available from several places without having to buy a PS2 nor the kit (email me privately for some URLs.) Also, someone recently reversed engineered the PS2 Dual Shock 2 API for native PS2 deving. I can email you the tarball if you want it. Did you look in Mac land? They're usually on the ball with the human interface side of things. But I don't yet know where to look for Mac API docs (maybe the Darwin site has force feedback device driver sources or API docs?) Force feedback devices were all the rage these past few years in Windows game land. Maybe you could look in MSDN for helpful hints from DirectX. Don't look to hard, or you might borrow some useless Microsoftism we could do without. After all this research, just provide an API for this type of device, make it portable and generic enough, and leave it to UI and application developers to worry about what to do with it. Don't sweat it if you yourself can't find a *nix use for the device. > Should this be classed as an "input" device - when it > only does output? Any clues anyone... Take a break and shug a beer (or two, or three.) You've been thinking too hard. :-) Not trying to insult you, but if it only does output, it's obviously an output device, dude. - -- RC5Stint <rc5...@ya...> PGP Key ID: 0x3B4BEC6F -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.6 (MingW32) - WinPT 0.5.1 Comment: For info see http://www.gnupg.org iD8DBQE8ZuJtifMcQztL7G8RArNvAJoC25ACl2ndAAwsjhRv+TkRzYv9+ACdFstJ rKw1h332n3nqVgxe8STxxos= =sffX -----END PGP SIGNATURE----- _________________________________________________________ Do You Yahoo!? Get your free @yahoo.com address at http://mail.yahoo.com |
From: Adrian M. <ad...@mc...> - 2002-02-10 18:35:06
|
On Sunday 10 Feb 2002 5:41 pm, M. R. Brown wrote: > * Adrian McMenamin <ad...@mc...> on Sun, Feb 10, 2002: > > Having played "Sonic Adventure" a bit I have come to the conclusion it > > just rocks about (a lot of money for a computer driven motor, but that's > > another story). > > > > The thing is - I cannot find a standard API/IF for this sort of thing for > > Unix/Linux. I could write a custom one, but what's the point of that? > > Look for the ruby module of the Linux Console project > (linuxconsole.sf.net). It contains a working FF api for 2.5. I don't know > how feasible it would be to backport it to 2.4 or if such a port already > exists - you may want to contact the FF author. > > M. R. It's already in the input.h that comes with the latest LinuxDC/SH sources - so the answer is - yes, treat it as an input device - and then I can implement a very much paired down version of the API (as it does far more than seems to be supported by the iForce devices which this is modelled on). I hadn't thought to look directly in the existing 2.4 sources, but its there.... |
From: M. R. B. <mr...@0x...> - 2002-02-10 17:43:44
|
* Adrian McMenamin <ad...@mc...> on Sun, Feb 10, 2002: >=20 > Having played "Sonic Adventure" a bit I have come to the conclusion it ju= st=20 > rocks about (a lot of money for a computer driven motor, but that's anoth= er=20 > story). >=20 > The thing is - I cannot find a standard API/IF for this sort of thing for= =20 > Unix/Linux. I could write a custom one, but what's the point of that? >=20 Look for the ruby module of the Linux Console project (linuxconsole.sf.net). It contains a working FF api for 2.5. I don't know how feasible it would be to backport it to 2.4 or if such a port already exists - you may want to contact the FF author. M. R. |
From: Adrian M. <ad...@mc...> - 2002-02-10 17:19:10
|
On Sunday 10 Feb 2002 1:21 pm, Adrian McMenamin wrote: > This seems very easy to do and is almost 'done' in the sense I have written > a driver that spins this thing around. > > Can anyone tell me what functions the puru does - ie does it just rock for > variable amounts of time or does it do something more? > > Adrian > Having played "Sonic Adventure" a bit I have come to the conclusion it just rocks about (a lot of money for a computer driven motor, but that's another story). The thing is - I cannot find a standard API/IF for this sort of thing for Unix/Linux. I could write a custom one, but what's the point of that? If the idea is to have games/software that is as portable as possible then it doesn't make sense. Should this be classed as an "input" device - when it only does output? Any clues anyone... Adrian |
From: Adrian M. <ad...@mc...> - 2002-02-10 13:25:12
|
This seems very easy to do and is almost 'done' in the sense I have written a driver that spins this thing around. Can anyone tell me what functions the puru does - ie does it just rock for variable amounts of time or does it do something more? Adrian |
From: M. R. B. <mr...@0x...> - 2002-02-10 02:13:42
|
* mag <ma...@ro...> on Sat, Feb 09, 2002: > ... Uniform CD-ROM driver Revision: > 3.12 SEGA Dreamcast GD-ROM > driver AICA audio > device driver for Dreamcast Linux - ver 0.1-pre15 > SEGA Dreamcast MAPLE Bus > drivers NET4: Linux > TCP/IP 1.0 for NET4.0 IP > Protocols: ICMP, UDP, > TCP IP: routing > cache hash table of 512 buckets, 4Kbytes > TCP: Hash tables configured (established 1024 bind > 2048) NET4: Unix domain sockets 1.0/SMP for > Linux NET4.0. GD-ROM: Got larger result > Hmm, so can I safely assume that you're using the linux-sh-dc from CVS? Even though we never officially said it, the linux-sh-dc is highly unstable and experimental and is subject to random breakage (yeah, I know development isn't *that* active, but the warning stands), so it's only recommended for developers. Of course the problem you're describing here has nothing to do with the status of the tree, but I just wanted to relay our official position on using it. > And that's it. It is not frozen or anything, (it registers maple > (dis)connect events), but it's not going anywhere either. The "GD-ROM: > Got larger result" message looks suspicios, but as far as i could tell > from briefly looking at the code, is not fatal. > Not fatal, but possibly dangerous. It looks like it prints this message when the command int handler receives the result from a command bigger than what's expected. In this case it truncates that data, and that's where the danger comes in. > Another idea that crossed my mind, was that this could be caused by me > trying to use devfs, but not having the devfsd (yet). But I don't think > that that's the problem, because I am pretty sure it does not even get > to the first startup scripts anyway, and that's where it is normally > started. Could you let us know if it still fails if you use the manually created device node (`mknod gdrom b 250 0')? Also, I've attached a patch that prints out GD-ROM command values in hex. If the problem persists, could you send a dump of the command values printed? If it's something more, I'll try to look into it. M. R. |
From: mag <ma...@ro...> - 2002-02-10 01:39:41
|
Hi, I can't seem to get my dreamcast to boot directly from the gdrom (without a ramdisk). It starts booting fine, but then it gets to about the point where it should be mounting the root, and just stops: ... Uniform CD-ROM driver Revision: 3.12 SEGA Dreamcast GD-ROM driver AICA audio device driver for Dreamcast Linux - ver 0.1-pre15 SEGA Dreamcast MAPLE Bus drivers NET4: Linux TCP/IP 1.0 for NET4.0 IP Protocols: ICMP, UDP, TCP IP: routing cache hash table of 512 buckets, 4Kbytes TCP: Hash tables configured (established 1024 bind 2048) NET4: Unix domain sockets 1.0/SMP for Linux NET4.0. GD-ROM: Got larger result And that's it. It is not frozen or anything, (it registers maple (dis)connect events), but it's not going anywhere either. The "GD-ROM: Got larger result" message looks suspicios, but as far as i could tell from briefly looking at the code, is not fatal. Another idea that crossed my mind, was that this could be caused by me trying to use devfs, but not having the devfsd (yet). But I don't think that that's the problem, because I am pretty sure it does not even get to the first startup scripts anyway, and that's where it is normally started. I am using the following kernel parameters: console=ttySC1,115200 mem=16M debug root=/dev/gdrom ro Any hints? Thanks in advance, mag |