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: Paul M. <pm...@mv...> - 2002-01-27 04:09:02
|
On Sat, Jan 26, 2002 at 04:26:51PM -0800, Fredrik Hubinette wrote: > Any complaints about the new maple stuff so far? Actually, haven't had too much of a chance to play with it yet.. will proba= bly get around to it soon.. > I've started reading up on block and mtd devices, > and it seems to me that the mtd subsystem really > isn't very well suited for writing a VMU driver. >=20 Curious how you come to this conclusion, considering the MTD subsystem's ve= ry purpose is for managing things like the VMU.. > In particular, the mtd subsystem seems to use driver api > which writes one byte at a time, which is something > I would really really like to avoid in a VMU driver. >=20 No, this is not the MTD subsystem. MTD supports many different types of devices.. the mapping driver is really nothing more than something that dea= ls with underlying access to the device. ie, it generally needs to manage thin= gs like varying offsets and such for read/write requests.. The one byte at a time thing isn't true either, I only implemented an 8-bit read/write to demonstrate how to deal with things.. the whole driver was a proof of concept that was pending a real maple implementation, and is still= in need of a bit of rework. Though full 8/16/32bit requests are fully supporte= d.. > In my oppinion it would seem better to use a block device > driver, but this may have other traps of course. MTD fully supports block devices. Devices are generally registered through mtdchar, which gives character device access to registered devices.. but you can just as easily build in mtdblock support, load that, and have all your registered devices accessible as block devices. > One particular issue with a VMU driver is that it needs > to convey some meta-information (size, block size, root > block location etc.) to the reader/filesystem. One way > to support this would be to use an ioctl, but I'm not sure > that would be the best way because then loopbacks would > be impossible once a VMU filesystem is written..... >=20 Size is static for all VMU's, since even the banked ones are only toggleable via a hardware switch. As far as block size (as far as the fs is concerned), this can all be mapped over top of the VMU flash.. all access to the device= is forced through maple blocks anyways, so it really doesn't matter what you w= ant to deal with your fs block size as.. it all gets wrapped in the end.. > An alternative solution (which is not pretty, but it works) > would be to map the meta-information into block 0 and then > map block 0 of the VMU into block 1 of the block device > (etc. etc.). >=20 Why would you want to store any of this information in a block on the devic= e? Your superblock already does all of this for you. Though for things like the VMU file system, where the superblock resides at the _end_ of the flash .. things get a little wierder. > And, before I forget: If I do make a vmu driver, what major/minor > numbers should I use? In theory you can connect 24 vmus to a > dreamcast, so I'll need at least 24 separate devices. (Alternatively, > I could create *one* device node which maps all the vmus into one > address space, but I would only do that as a last resort...) >=20 MTD already deals with this. Don't side step it. And if you're lucky .. I'll even find the free time to fix my driver to work with your maple code, and then you won't need to worry about this at all. (This is also why I told yo= u a good while ago that a driver was already in place and was merely in need of= a bit of fixing up.. reinventing the wheel is a completely useless activity here). Regards, --=20 Paul Mundt <pm...@mv...> MontaVista Software, Inc. |
From: M. R. B. <mr...@0x...> - 2002-01-27 01:22:17
|
* Fredrik Hubinette <hu...@hu...> on Sat, Jan 26, 2002: > > > In particular, the mtd subsystem seems to use driver api > > > which writes one byte at a time, which is something > > > I would really really like to avoid in a VMU driver. > > >=20 > >=20 > > Where did you see this? That MTD couldn't handle block devices? >=20 > Well, I might be reading the whole thing wrong, but as I understand > the code, it provides both char and block devices for each mtd device. > However, the mtd map handler has a byte-based interface which seems > like a lot of extra work to implement. This may however be incorrect. > I will do some more reading, but maybe someone more knowledge can save > me some trouble by pointing me in the right direction? >=20 I remember now. Paul's idea was to read/write from the VMU in blocks, but provide the byte-interface for the currently held block. If you need byte 512, you'd have to read in that block in order to get to it. Sound sane? It's complicated. I'd wait for his answer, or see if any other MTD devices do anything similiar. M. R. |
From: Fredrik H. <hu...@hu...> - 2002-01-27 01:15:48
|
> > In particular, the mtd subsystem seems to use driver api > > which writes one byte at a time, which is something > > I would really really like to avoid in a VMU driver. > > > > Where did you see this? That MTD couldn't handle block devices? Well, I might be reading the whole thing wrong, but as I understand the code, it provides both char and block devices for each mtd device. However, the mtd map handler has a byte-based interface which seems like a lot of extra work to implement. This may however be incorrect. I will do some more reading, but maybe someone more knowledge can save me some trouble by pointing me in the right direction? If this is indeed a misunderstanding on my behalf, then I will definitely use the MTD subsystem since that is probably where vmus *should* be integrated. > > In my oppinion it would seem better to use a block device > > driver, but this may have other traps of course. > > One particular issue with a VMU driver is that it needs > > to convey some meta-information (size, block size, root > > block location etc.) to the reader/filesystem. One way > > to support this would be to use an ioctl, but I'm not sure > > that would be the best way because then loopbacks would > > be impossible once a VMU filesystem is written..... > > > > How is conveying metainformation different than any other block device in > existance? I'm not sure I follow. If you dd from a CD-ROM drive to a > file, you'll only get the size of that CD - the metainformation is > privately manipulated by the block driver itself. It's not that different. But with a cdrom you *know* where the root block is without asking the block device. If you dd a iso9660 fs to a file, you can still mount it using the loopback device. On the other hand to be strictly compliant, a dreamcast vmu filesystem has to ask the device where the root block is. This meta-information would not survive a 'dd' operation, and thus the file would not be loopback-mountable unless you specified where the root block is manually. Of course specifying the root block manually would probably be acceptable. Since almost all VMU's have the root block in the same place, a reasonable default would practically solve the problem. > > An alternative solution (which is not pretty, but it works) > > would be to map the meta-information into block 0 and then > > map block 0 of the VMU into block 1 of the block device > > (etc. etc.). > > > > Map where? I thought you ditched MTD :P. Just kidding. I'm not so sure > you want to give up on MTD, have you taken a look at the initial work Paul > has done in CVS? I have looked at it. I may not understand it properly, but it seems very incomplete and based on reading/writing one byte at a time, which would be extremely slow. It was part of the reason I thought the mtdmap API was byte-based. /Fredrik Hubinette |
From: Adrian M. <ad...@mc...> - 2002-01-27 01:00:09
|
C++ style (actually C99 compliant :->) comments removed/replaced: --- main.c Sat Jan 26 11:38:03 2002 +++ aica.c Sun Jan 27 00:46:10 2002 @@ -32,7 +32,7 @@ #include <asm/semaphore.h> #include <asm/dc_sysasic.h> #include "../sound_config.h" -#include "arm7.h" //static array of ARM 7 code +#include "arm7.h" /* Command values */ #define AICA_CMD_KICK 0x80000000 @@ -177,17 +177,17 @@ } + +/* Stop the ARM7 processor and clear registers + for all channels */ void spu_disable() { int i; - - /* Stop the ARM processor */ spu_write_wait(); uint32_t regval = readl(0xa0702c00); regval |= 1; spu_write_wait(); writel(regval, 0xa0702c00); - //close down all channels for (i = 0; i < 64; i++) { spu_write_wait(); regval = readl(0xa0700000 + (i * 0x80)); @@ -200,26 +200,19 @@ } +/* Halt the sound processor, + clear the memory, + load some default ARM7 code, + and then restart ARM7 +*/ int spu_init() { - /* Stop the ARM */ + spu_disable(); - - /* Clear out sound RAM */ spu_memset(0, 0, 0x200000 / 4); - - /* Load a default "program" into the SPU that just executes - an infinite loop */ *(uint32_t *) 0xa0800000 = 0xea000002; - - /* Start the SPU again */ spu_enable(); - - // Wait a little schedule(); - - - return 0; } @@ -251,52 +244,48 @@ typedef struct aica_dev { struct aica_dev *next_dev; - int channel; //which one are we using here? - int audio_minor; //device number - int mixer_minor; //device number - wait_queue_head_t open_wait; //wait queue - int last_write_length; //length of data last written to device - int sformat; //format of data being written + int channel; /* which one are we using here? */ + int audio_minor; /* device number */ + int mixer_minor; /* device number */ + wait_queue_head_t open_wait; /* wait queue */ + int last_write_length; /* length of data last written to device */ + int sformat; /* format of data being written */ } aica_dev_t; -//static linked list of all open devices +/* List of all open devices */ static aica_dev_t *aica_dev_list; +/* open the device file, locking AICA registers as we go */ + static int aica_audio_open(struct inode *inode, struct file *file) { aica_dev_t *devc; dev_t minor = MINOR(inode->i_rdev); MOD_INC_USE_COUNT; - /* open the channel - * locking the memory as appropriate - */ + devc = aica_dev_list; if (down_interruptible(&dsp_mutex)) return -ERESTARTSYS; - - //Lock channels 1 & 2 + request_mem_region(0xa0700000, 0x100, "AICA Channels"); - //sample type - based on which device has been opened chanh = kmalloc(sizeof(aica_channel), GFP_KERNEL); if (chanh == NULL) return -ENOMEM; chanh->flags = 0; if ((minor & 0xF) == SND_DEV_DSP) { - chanh->sfmt = SM_8BIT; //AICA format - devc->sformat = AFMT_U8; //OSS default format for this device + chanh->sfmt = SM_8BIT; + devc->sformat = AFMT_U8; } else if ((minor & 0xF) == SND_DEV_DSP16) { chanh->sfmt = SM_16BIT; devc->sformat = AFMT_S16_LE; } else { - //total failure! DEBGM("Attempting to open an unsupported device file\n"); - //unlock channel release_mem_region(0xa0700000, 0x100); up(&dsp_mutex); MOD_DEC_USE_COUNT; @@ -306,7 +295,6 @@ devc->last_write_length = 0; file->private_data = devc; - //Start AICA spu_disable(); spu_memset(0, 0, 0x31000); spu_memload(0, bin_arm7, sizeof(bin_arm7)); @@ -317,12 +305,15 @@ chanh->vol = left_volume; chanh->pan = 0x80; chanh->pos = 0; - chanh->freq = 8000; //8k is defined as the OSS default + chanh->freq = 8000; total_count = 0; currentpoint = 0; return 0; } + +/* Release device, waiting for queued audio to play out */ + static int aica_audio_release(struct inode *inode, struct file *file) { aica_dev_t *devc = (aica_dev_t *) (file->private_data); @@ -335,8 +326,6 @@ playpoint = readl(0xa0810004 + 4); if ((playpoint * samplelength) > currentpoint) { for (i = 0; i < (HZ * 2); i++) { - //wait comes back to start of buffer - interruptible_sleep_on_timeout(& (devc-> open_wait), @@ -362,7 +351,6 @@ break; } } - //reached the end of the road spu_disable(); if (BUFFER) @@ -386,13 +374,13 @@ return 0; } + +/* Perform stereo seperation for 16- and 8-bit samples */ static int aica_audio_process_stereo(int *count_out, int *count) { - - //second channel has largely the same attributes as first int z, y, x, w; *count_out = *count / 2; - y = *count_out; //only need to read this once + y = *count_out; BUFFER2 = kmalloc(y, GFP_KERNEL); if (BUFFER2 == NULL) return -1; @@ -424,10 +412,12 @@ return 0; } - +/* Block if buffer full until playing has freed + up necessary space. Sleep while waiting for + low smaple rates, but lock processor for + demaning samples */ inline static int aica_audio_tidy_buffers(aica_dev_t * devc) { - //about to overwrite a playing buffer? int playpoint; do { if (chanh->freq < 23000) @@ -438,10 +428,8 @@ } while (((playpoint * samplelength) > currentpoint) && ((playpoint * samplelength) < (currentpoint + total_count))); - //writing past end of the buffer? if ((currentpoint + total_count) > 0x8000) { currentpoint = 0; - //check again do { if (chanh->freq < 23000) interruptible_sleep_on_timeout(& @@ -467,13 +455,11 @@ BUFFER = kmalloc(count, GFP_KERNEL); copy_from_user(BUFFER, buffer, count); int count_out = count; - //check for any format changes needed samplelength = 1; if (chanh->sfmt == SM_16BIT) samplelength = 2; else if (devc->sformat == AFMT_U8) convert_u8tos8(BUFFER, count); - //process stereo if required if (stereo) { if (aica_audio_process_stereo(&count_out, &count) != 0) return -1; @@ -515,9 +501,11 @@ int newdata, left, right, s, m; audio_buf_info info; switch (cmd) { + case SNDCTL_DSP_GETCAPS: put_user(DSP_CAP_TRIGGER | DSP_CAP_REALTIME, (int *) arg); return 0; + case SNDCTL_DSP_SETFMT: get_user(newdata, (int *) arg); switch (newdata) { @@ -540,16 +528,18 @@ default: devc->sformat = AFMT_U8; chanh->sfmt = SM_8BIT; - //have set a default format - //but should return an error + /* have set a default format + but should return an error */ return -ENOTTY; } put_user(devc->sformat, (int *) arg); return 0; + case SNDCTL_DSP_GETFMTS: newdata = AFMT_S8 | AFMT_S16_LE | AFMT_IMA_ADPCM; put_user(newdata, (int *) arg); return 0; + case SNDCTL_DSP_SPEED: get_user(newdata, (int *) arg); if (newdata < 10000) { @@ -570,16 +560,18 @@ chanh->freq = 44100; put_user(chanh->freq, (int *) arg); return 0; + case SNDCTL_DSP_RESET: chn_halt(); - //clear memory spu_memset(0x21000, 0, 0x8000); spu_memset(0x11000, 0, 0x8000); currentpoint = 0; return 0; + case SNDCTL_DSP_GETBLKSIZE: put_user(buffer_size, (int *) arg); return 0; + case SNDCTL_DSP_SETFRAGMENT: get_user(newdata, (int *) arg); s = newdata & 0xffff; @@ -592,8 +584,9 @@ arg = (m << 16) | s; put_user(0x010f, (int *) arg); return 0; + + /* bytes left to play */ case SNDCTL_DSP_GETODELAY: - //bytes left to play spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -603,8 +596,10 @@ newdata = currentpoint - (s * samplelength); put_user(newdata, (int *) arg); return 0; + + /* How many fragments till writing blocks? */ case SNDCTL_DSP_GETOSPACE: - //how many framents till writing blocks? + spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -615,15 +610,14 @@ info.fragments = ((0x8000 - currentpoint) + (s * samplelength)) / buffer_size; - - info.fragstotal = 0x8000 / buffer_size; //total fragments possible + info.fragstotal = 0x8000 / buffer_size; info.fragsize = buffer_size; info.bytes = info.fragments * buffer_size; copy_to_user((audio_buf_info *) arg, &info, sizeof(info)); return 0; + case SNDCTL_DSP_SYNC: case SNDCTL_DSP_POST: - //flushes sound buffer spu_write_wait(); s = readl(0xa0810004 + 4); if ((s * samplelength) > currentpoint) { @@ -631,20 +625,18 @@ currentpoint + 0x8000 - (s * samplelength); } else newdata = currentpoint - (s * samplelength); - //work out delay newdata = newdata / samplelength; - //in milliseconds + /* delay in milliseconds */ newdata = (newdata * 1000) / chanh->freq; - //sleep that time then kill sound newdata = (newdata * HZ) / 1000; interruptible_sleep_on_timeout(&(devc->open_wait), newdata); chn_halt(); - //clear memory spu_memset(0x21000, 0, 0x8000); spu_memset(0x11000, 0, 0x8000); currentpoint = 0; return 0; + case SNDCTL_DSP_STEREO: get_user(newdata, (int *) arg); if (newdata == 1) { @@ -662,20 +654,20 @@ newdata = 1; put_user(newdata, (int *) arg); return 0; - //Supported Mixer ioctls + + /* Supported Mixer ioctls */ case SOUND_MIXER_READ_DEVMASK: newdata = SOUND_MASK_PCM | SOUND_MASK_VOLUME; put_user(newdata, (int *) arg); return 0; + case SOUND_MIXER_WRITE_VOLUME: case SOUND_MIXER_WRITE_PCM: - get_user(newdata, (int *) arg); - //Stereo channel volume + get_user(newdata, (int *) arg); left = newdata & 0xff; right = (newdata & 0xff00) >> 8; left_volume = left; right_volume = right; - //rescale and set chanh->vol = (left * 255) / 100; newdata = ((right * 255) / 100) << 8; newdata = newdata | 0xff; @@ -701,7 +693,9 @@ }; -//Mixer code + +/* Mixer code + very basic currently */ static int aica_mixer_open(struct inode *inode, struct file *file) { @@ -723,19 +717,19 @@ return -1; int newdata, left, right; switch (cmd) { + case SOUND_MIXER_READ_DEVMASK: newdata = SOUND_MASK_PCM | SOUND_MASK_VOLUME; put_user(newdata, (int *) arg); return 0; + case SOUND_MIXER_WRITE_VOLUME: case SOUND_MIXER_WRITE_PCM: get_user(newdata, (int *) arg); - //Stereo channel volume left = newdata & 0xff; right = (newdata & 0xff00) >> 8; left_volume = left; right_volume = right; - //rescale and set chanh->vol = (left * 255) / 100; newdata = ((right * 255) / 100) << 8; newdata = newdata | 0xff; @@ -757,11 +751,18 @@ ioctl:aica_mixer_ioctl, }; + + +/* Create holder for device, + together with wait queue + and then register device + with underlying OSS code +*/ + static int __init attach_aica(void) { printk - ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre14\n"); - //initialise data holder for device + ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre14\n"); aica_dev_t *devc = NULL; sema_init(&dsp_mutex, 1); int err = -ENOMEM; @@ -770,17 +771,13 @@ if (devc == NULL) goto fail0; - //initialise wait queue init_waitqueue_head(&devc->open_wait); - //register device drivers - //register sound_dsp with OSS devc->audio_minor = register_sound_dsp(&aica_audio_fops, -1); if (devc->audio_minor < 0) { DEBGM("attach_aica: register_sound_dsp error %d\n", err); err = devc->audio_minor; goto faildsp; } - //register mixer with OSS devc->mixer_minor = register_sound_mixer(&aica_mixer_fops, -1); if (devc->mixer_minor < 0) { DEBGM("attach_aica: register_sound_mixer error %d\n", err); @@ -815,8 +812,6 @@ static int __exit unload_aica(void) { - //walk through the list - //deallocating the lot aica_dev_t *devc; aica_dev_t *devcp; devcp = aica_dev_list; @@ -835,6 +830,12 @@ return 0; } + + +/* Lock iomem and initialise + ARM7 processor +*/ + static int __init init_aica(void) { @@ -842,23 +843,19 @@ BUFFER2 = NULL; BUFFERL = NULL; BUFFERR = NULL; - /* take ownership of register that controls ARM */ if (request_mem_region(0xa0702c00, 4, "AICA ARM control") == NULL) { DEBGM ("Could not take ownership of SPU control register\n"); return -ENODEV; } -/* take ownership of sound memory */ if (request_mem_region(0xa0800000, 0x200000, "AICA Sound RAM") == NULL) { DEBGM("Could not take ownership of sound RAM\n"); return -ENOMEM; } - //Basic spu set up spu_init(); - //now create the devfs files etc if (attach_aica()) { DEBGM("Failed to initialise AICA sound driver\n"); return -EINVAL; @@ -869,19 +866,14 @@ static void __exit exit_aica(void) { -//set low bit of register to 1 DEBGM("AICA sound driver being unloaded...\n"); - if (unload_aica()) DEBGM("Error in unloading AICA\n"); - spu_init(); //revert to neutral state - + spu_init(); -/* //release the SPU control register */ release_mem_region(0xa0702c00, 4); - //release the sound memory release_mem_region(0xa0800000, 0x200000); } |
From: M. R. B. <mr...@0x...> - 2002-01-27 00:51:48
|
* Fredrik Hubinette <hu...@hu...> on Sat, Jan 26, 2002: >=20 > Any complaints about the new maple stuff so far? > I've started reading up on block and mtd devices, > and it seems to me that the mtd subsystem really > isn't very well suited for writing a VMU driver. >=20 I haven't had a chance to test anything, I'm still tied up with another project, probably for another week. I booted it a few times after I committed your patch, I didn't run into any snags, except for the module issue. > In particular, the mtd subsystem seems to use driver api > which writes one byte at a time, which is something > I would really really like to avoid in a VMU driver. >=20 Where did you see this? That MTD couldn't handle block devices? > In my oppinion it would seem better to use a block device > driver, but this may have other traps of course. > One particular issue with a VMU driver is that it needs > to convey some meta-information (size, block size, root > block location etc.) to the reader/filesystem. One way > to support this would be to use an ioctl, but I'm not sure > that would be the best way because then loopbacks would > be impossible once a VMU filesystem is written..... >=20 How is conveying metainformation different than any other block device in existance? I'm not sure I follow. If you dd from a CD-ROM drive to a file, you'll only get the size of that CD - the metainformation is privately manipulated by the block driver itself. > An alternative solution (which is not pretty, but it works) > would be to map the meta-information into block 0 and then > map block 0 of the VMU into block 1 of the block device > (etc. etc.). >=20 Map where? I thought you ditched MTD :P. Just kidding. I'm not so sure you want to give up on MTD, have you taken a look at the initial work Paul has done in CVS? =20 > And, before I forget: If I do make a vmu driver, what major/minor > numbers should I use? In theory you can connect 24 vmus to a > dreamcast, so I'll need at least 24 separate devices. (Alternatively, > I could create *one* device node which maps all the vmus into one > address space, but I would only do that as a last resort...) >=20 Hmm, check linux/Documentation/devices.txt for unused numbers. You can also use devfs - I'm not so sure on the API, but that documentation is in the kernel tree as well. > What do you people think? Since I'm fairly new to linux kernel > hacking I apreciate your input on these ideas. Paul had a pretty good bead on things, he'll probably reply with something more intelligible than what I wrote here. M. R. |
From: Fredrik H. <hu...@hu...> - 2002-01-27 00:26:58
|
Any complaints about the new maple stuff so far? I've started reading up on block and mtd devices, and it seems to me that the mtd subsystem really isn't very well suited for writing a VMU driver. In particular, the mtd subsystem seems to use driver api which writes one byte at a time, which is something I would really really like to avoid in a VMU driver. In my oppinion it would seem better to use a block device driver, but this may have other traps of course. One particular issue with a VMU driver is that it needs to convey some meta-information (size, block size, root block location etc.) to the reader/filesystem. One way to support this would be to use an ioctl, but I'm not sure that would be the best way because then loopbacks would be impossible once a VMU filesystem is written..... An alternative solution (which is not pretty, but it works) would be to map the meta-information into block 0 and then map block 0 of the VMU into block 1 of the block device (etc. etc.). And, before I forget: If I do make a vmu driver, what major/minor numbers should I use? In theory you can connect 24 vmus to a dreamcast, so I'll need at least 24 separate devices. (Alternatively, I could create *one* device node which maps all the vmus into one address space, but I would only do that as a last resort...) What do you people think? Since I'm fairly new to linux kernel hacking I apreciate your input on these ideas. /Hubbe |
From: M. R. B. <mr...@0x...> - 2002-01-26 23:51:37
|
* Fredrik Hubinette <hu...@hu...> on Sat, Jan 26, 2002: >=20 > How about replacing all those C++-comments with C-comments? > Not that they bother GCC, but it's just not right to use > C++-comments in C-code... >=20 When I get back from webdev-hell (another week or so) one of the first things I plan to do is get rid of the C++ comments. To Adrian: > The comments are perfectly good C comments. See the C99 standard. They are NOT accepted in the kernel. Period. If you still feel unsure, post an innocent question about them to the linux-kernel mailing list, and see how many negative replies you get. Even if you got a couple of positive responses (which you won't), we won't accept them in practice in this tree or in the LinuxSH tree. Not trying to be rude or anything, but standards have nothing to do with it. Please, in the future, do not use them. Thanks, M. R. |
From: Adrian M. <ad...@mc...> - 2002-01-26 23:29:34
|
On Saturday 26 Jan 2002 11:25 pm, Paul Mundt wrote: > On Sat, Jan 26, 2002 at 11:12:40PM +0000, Adrian McMenamin wrote: > > > How about replacing all those C++-comments with C-comments? > > > Not that they bother GCC, but it's just not right to use > > > C++-comments in C-code... > > > > The comments are perfectly good C comments. See the C99 standard. > > C99 compliant, perhaps. Strict ANSI compliant on the other hand... not even > close. C99 allows for all sorts of stupidities, most of which have no place > in the kernel IMO. > > As a good practice, try building w/ -Werror -ansi to your CFLAGS and see > how compliant your code is. (large portions of the kernel fail with this, > but that doesn't make it "clean" or "proper"). > > Regards, So American standards institute doesn't like it? Come on. :-> Standards are standards. End of story. Adrian PS The driver also uses the recommended C99 typing too. |
From: Paul M. <pm...@mv...> - 2002-01-26 23:24:06
|
On Sat, Jan 26, 2002 at 11:12:40PM +0000, Adrian McMenamin wrote: > > How about replacing all those C++-comments with C-comments? > > Not that they bother GCC, but it's just not right to use > > C++-comments in C-code... >=20 > The comments are perfectly good C comments. See the C99 standard. >=20 C99 compliant, perhaps. Strict ANSI compliant on the other hand... not even close. C99 allows for all sorts of stupidities, most of which have no place= in the kernel IMO. As a good practice, try building w/ -Werror -ansi to your CFLAGS and see how compliant your code is. (large portions of the kernel fail with this, but t= hat doesn't make it "clean" or "proper"). Regards, --=20 Paul Mundt <pm...@mv...> MontaVista Software, Inc. |
From: Adrian M. <ad...@mc...> - 2002-01-26 23:15:10
|
On Saturday 26 Jan 2002 10:56 pm, Fredrik Hubinette wrote: > How about replacing all those C++-comments with C-comments? > Not that they bother GCC, but it's just not right to use > C++-comments in C-code... > > /Fredrik Hubinette > > _______________________________________________ > Linuxdc-dev mailing list > Lin...@li... > https://lists.sourceforge.net/lists/listinfo/linuxdc-dev The comments are perfectly good C comments. See the C99 standard. |
From: Paul M. <pm...@mv...> - 2002-01-26 23:06:05
|
On Sat, Jan 26, 2002 at 02:56:38PM -0800, Fredrik Hubinette wrote: > How about replacing all those C++-comments with C-comments? > Not that they bother GCC, but it's just not right to use > C++-comments in C-code... It's times like these I wish we had -ansi and -Werror back in the CFLAGS.. = in which event, the kernel wouldn't build with C++ style comments.. Regards, --=20 Paul Mundt <pm...@mv...> MontaVista Software, Inc. |
From: Fredrik H. <hu...@hu...> - 2002-01-26 22:57:11
|
How about replacing all those C++-comments with C-comments? Not that they bother GCC, but it's just not right to use C++-comments in C-code... /Fredrik Hubinette |
From: Adrian M. <ad...@mc...> - 2002-01-26 13:35:15
|
On Saturday 26 Jan 2002 1:19 pm, M. R. Brown wrote: > * Adrian McMenamin <ad...@mc...> on Sat, Jan 26, 2002: > > I did these sequentially this time - so they were a few minutes apart. > > But I'll do them together in future. Sorry for any inconvenience. > > There is a reason for me being so pendantic - this is the closest we can > get to "changesets" in CVS. Each functional commit to the repository > should have a logical block that describes it in ChangeLog.dc. We can then > use the date of checkin or other CVS info to revert whole changes at once. > > Eventually I'll write all this stuff down. > > M. R. I don't think you are being pedantic. It all makes sense to me, so I'll do it in future. Adrian |
From: M. R. B. <mr...@0x...> - 2002-01-26 13:19:18
|
* Adrian McMenamin <ad...@mc...> on Sat, Jan 26, 2002: > I did these sequentially this time - so they were a few minutes apart. Bu= t=20 > I'll do them together in future. Sorry for any inconvenience. >=20 There is a reason for me being so pendantic - this is the closest we can get to "changesets" in CVS. Each functional commit to the repository should have a logical block that describes it in ChangeLog.dc. We can then use the date of checkin or other CVS info to revert whole changes at once. Eventually I'll write all this stuff down. M. R. |
From: Adrian M. <ad...@mc...> - 2002-01-26 13:05:09
|
On Saturday 26 Jan 2002 12:56 pm, M. R. Brown wrote: > * Adrian McMenamin <ad...@mc...> on Sat, Jan 26, 2002: > > This patch merely cleans up some (more) dead code, it should not have any > > impact on the functuality, (though the open command should execute a bit > > faster, but that is not a performance critical issue for this driver). > > > > Given the minor nature of this I will commit to the CVS in the next few > > hours - I've tested it already - but anyone spots an error please let me > > know ASAP. > > Please remember to add an appropiate entry to ChangeLog.dc and commit > ChangeLog.dc along with the rest of your changes (not seperately, in the > same commit). > > Thanks, > > M. R. I did these sequentially this time - so they were a few minutes apart. But I'll do them together in future. Sorry for any inconvenience. Adrian |
From: M. R. B. <mr...@0x...> - 2002-01-26 12:56:35
|
* Adrian McMenamin <ad...@mc...> on Sat, Jan 26, 2002: > This patch merely cleans up some (more) dead code, it should not have any= =20 > impact on the functuality, (though the open command should execute a bit= =20 > faster, but that is not a performance critical issue for this driver). >=20 > Given the minor nature of this I will commit to the CVS in the next few h= ours=20 > - I've tested it already - but anyone spots an error please let me know A= SAP. >=20 Please remember to add an appropiate entry to ChangeLog.dc and commit ChangeLog.dc along with the rest of your changes (not seperately, in the same commit). Thanks, M. R. |
From: Adrian M. <ad...@mc...> - 2002-01-26 11:45:10
|
This patch merely cleans up some (more) dead code, it should not have any impact on the functuality, (though the open command should execute a bit faster, but that is not a performance critical issue for this driver). Given the minor nature of this I will commit to the CVS in the next few hours - I've tested it already - but anyone spots an error please let me know ASAP. Adrian --- main.c Fri Jan 18 19:08:42 2002 +++ aica.c Sat Jan 26 11:36:22 2002 @@ -31,7 +31,6 @@ #include <linux/ioctl.h> #include <asm/semaphore.h> #include <asm/dc_sysasic.h> - #include "../sound_config.h" #include "arm7.h" //static array of ARM 7 code @@ -43,14 +42,10 @@ #define AICA_CMD_VOL 3 /* Sound modes */ - #define SM_8BIT 1 #define SM_16BIT 0 #define SM_ADPCM 2 -#define _BUILD_DEBUG_ - - #ifdef _BUILD_DEBUG_ #define DEBGM(fmt, args...) (printk(KERN_ERR fmt, ##args)) @@ -269,49 +264,23 @@ static aica_dev_t *aica_dev_list; - -static struct aica_dev *aica_walk_devlist(void) -{ - //first get the current device - walking the device list - aica_dev_t *devc = aica_dev_list; - if (!devc) { - DEBGM("Failed to get an AICA device in walk through\n"); - return NULL; // for some reason it doesn't exist - } - while (devc->next_dev != NULL) - devc = devc->next_dev; - return devc; -} - - - - static int aica_audio_open(struct inode *inode, struct file *file) { - - aica_dev_t *devc; dev_t minor = MINOR(inode->i_rdev); MOD_INC_USE_COUNT; + /* open the channel * locking the memory as appropriate - * - * first get the current device - walking the list */ - - devc = aica_walk_devlist(); - if (!devc) { - MOD_DEC_USE_COUNT; - DEBGM("AICA: Could not walk list\n"); - return -ENODEV; - } + devc = aica_dev_list; + if (down_interruptible(&dsp_mutex)) return -ERESTARTSYS; //Lock channels 1 & 2 request_mem_region(0xa0700000, 0x100, "AICA Channels"); - //sample type - based on which device has been opened chanh = kmalloc(sizeof(aica_channel), GFP_KERNEL); if (chanh == NULL) @@ -334,7 +303,6 @@ return -EINVAL; } - devc->last_write_length = 0; file->private_data = devc; @@ -388,7 +356,6 @@ (devc-> open_wait), 1); - spu_write_wait(); playpoint = readl(0xa0810004 + 4); if ((playpoint * samplelength) > currentpoint) @@ -495,7 +462,6 @@ const char *buffer, size_t count, loff_t * ppos) { - aica_dev_t *devc = (aica_dev_t *) (file->private_data); stereo = chanh->flags & 1; BUFFER = kmalloc(count, GFP_KERNEL); @@ -726,9 +692,6 @@ } - - - static struct file_operations aica_audio_fops = { owner:THIS_MODULE, open:aica_audio_open, @@ -794,11 +757,10 @@ ioctl:aica_mixer_ioctl, }; - static int __init attach_aica(void) { printk - ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre13\n"); + ("AICA audio device driver for Dreamcast Linux - ver 0.1-pre14\n"); //initialise data holder for device aica_dev_t *devc = NULL; sema_init(&dsp_mutex, 1); @@ -873,7 +835,6 @@ return 0; } - static int __init init_aica(void) { @@ -906,9 +867,6 @@ return 0; } - - - static void __exit exit_aica(void) { //set low bit of register to 1 @@ -928,16 +886,8 @@ } - - - - module_init(init_aica); module_exit(exit_aica); - - - - MODULE_AUTHOR("Adrian McMenamin"); MODULE_DESCRIPTION("Basic OSS sound driver for Linux/DC"); |
From: Adrian M. <ad...@mc...> - 2002-01-23 20:27:03
|
On Wednesday 23 Jan 2002 7:51 pm, M. R. Brown wrote: > * Adrian McMenamin <ad...@mc...> on Wed, Jan 23, 2002: > > With all the traffic on the dcdev list about DivX players I wonder > > whether anyone here has gotten mplayer to play avis (as opposed to mpegs) > > on their DC. > > mplayer needs: > * Optimized SH4 IDCT routines and other SH4-specific optimizations > * YUV overlay support > * A speedier pvr2fb framebuffer that achieves 2D peak using DMA and store > queue blits > * A faster AICA driver > * To rid itself of it's x86-centric design > > This is a lot of work. > > M. R. Should not let best be the enemy of better here. Most of the traffic on dcdev is about how they can get no output. I agree about the need for optimisations, but they shouldn't bar us from crowing a bit. Can't we encourage the wider SH4 developer community to do at least some of this? Adrian |
From: M. R. B. <mr...@0x...> - 2002-01-23 19:51:12
|
* Adrian McMenamin <ad...@mc...> on Wed, Jan 23, 2002: > With all the traffic on the dcdev list about DivX players I wonder whethe= r=20 > anyone here has gotten mplayer to play avis (as opposed to mpegs) on thei= r DC. >=20 mplayer needs: * Optimized SH4 IDCT routines and other SH4-specific optimizations * YUV overlay support * A speedier pvr2fb framebuffer that achieves 2D peak using DMA and store queue blits * A faster AICA driver * To rid itself of it's x86-centric design This is a lot of work. M. R. |
From: Adrian M. <ad...@mc...> - 2002-01-23 19:36:48
|
With all the traffic on the dcdev list about DivX players I wonder whether anyone here has gotten mplayer to play avis (as opposed to mpegs) on their DC. Seems to me we have the superior "product" if we can do that.... Adrian |
From: M. R. B. <mr...@0x...> - 2002-01-23 07:09:04
|
* Fredrik Hubinette <hu...@hu...> on Tue, Jan 22, 2002: >=20 > This is a patch against the current (Jan 22 2002) linuxsh tree. > Some of the highlights include: >=20 > o All Maple devices are probed > o Lightgun Support included > o Drivers may support one or more functions > o Multiple drivers per device possible > (But only one per function) > o Uses DMA interrupt when possible >=20 Ok, I've imported it into CVS, although I screwed up the tags. You'll currently find this in HEAD as opposed to linux-sh-dc_2_4-maple like we previously discussed - it works out since developers now have a mostly working tree whereas Maple wasn't fully working before. I will test the keyboard and mouse portions in a bit. Need to go pick up a lightgun today or something. Thanks for the patch, M. R. |
From: Fredrik H. <hu...@hu...> - 2002-01-22 23:56:30
|
As per request, here is the maple patch in unified diff format: --------------------------------8<---------------------- diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/arch/sh/config.in ./arch/sh/config.in --- ../linux-sh-orig/arch/sh/config.in Fri Nov 2 16:52:47 2001 +++ ./arch/sh/config.in Mon Jan 7 22:22:18 2002 @@ -319,6 +319,7 @@ if [ "$CONFIG_INPUT" != "n" ]; then dep_tristate ' Maple Bus keyboard support' CONFIG_MAPLE_KEYBOARD $CONFIG_INPUT dep_tristate ' Maple Bus mouse support' CONFIG_MAPLE_MOUSE $CONFIG_INPUT + dep_tristate ' Maple Light Gun support' CONFIG_MAPLE_LIGHTGUN $CONFIG_INPUT else comment 'Input core support is required for Maple input peripherals' fi diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/Makefile ./drivers/char/Makefile --- ../linux-sh-orig/drivers/char/Makefile Fri Nov 30 15:03:52 2001 +++ ./drivers/char/Makefile Mon Jan 7 22:19:27 2002 @@ -178,6 +178,7 @@ obj-$(CONFIG_MAPLE_KEYBOARD) += maple_keyb.o obj-$(CONFIG_MAPLE_MOUSE) += maplemouse.o +obj-$(CONFIG_MAPLE_LIGHTGUN) += maple_lg.o obj-$(CONFIG_BUSMOUSE) += busmouse.o obj-$(CONFIG_DTLK) += dtlk.o diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/joystick/maplecontrol.c ./drivers/char/joystick/maplecontrol.c --- ../linux-sh-orig/drivers/char/joystick/maplecontrol.c Mon Oct 15 13:44:59 2001 +++ ./drivers/char/joystick/maplecontrol.c Tue Jan 15 05:17:29 2002 @@ -21,11 +21,11 @@ }; -static void dc_pad_callback(struct mapleq *mq) +static void dc_pad_callback(struct maple_driver_data *data) { unsigned short buttons; - struct maple_device *mapledev = mq->dev; - struct dc_pad *pad = mapledev->private_data; + struct mapleq *mq=&data->mq; + struct dc_pad *pad = data->private_data; struct input_dev *dev = &pad->dev; unsigned char *res = mq->recvbuf; @@ -73,10 +73,10 @@ } -static int dc_pad_connect(struct maple_device *dev) +static int dc_pad_connect(struct maple_driver_data *d) { int i; - unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); + unsigned long data = d->function_data; struct dc_pad *pad; const short btn_bit[32] = { @@ -97,7 +97,7 @@ return -1; memset(pad, 0, sizeof(struct dc_pad)); - dev->private_data = pad; + d->private_data = pad; for (i=0; i<32; i++) if (data&(1<<i) && btn_bit[i]>=0) @@ -132,13 +132,11 @@ pad->dev.close = dc_pad_close; pad->dev.event = NULL; - pad->dev.name = dev->product_name; + pad->dev.name = d->dev->product_name; pad->dev.idbus = BUS_MAPLE; input_register_device(&pad->dev); - maple_getcond_callback(dev, dc_pad_callback, 1, MAPLE_FUNC_CONTROLLER); - printk(KERN_INFO "input%d: controller(0x%lx): %s\n", pad->dev.number, data, pad->dev.name); @@ -148,9 +146,9 @@ } -static void dc_pad_disconnect(struct maple_device *dev) +static void dc_pad_disconnect(struct maple_driver_data *d) { - struct dc_pad *pad = dev->private_data; + struct dc_pad *pad = d->private_data; input_unregister_device(&pad->dev); @@ -165,6 +163,8 @@ name: "Dreamcast controller", connect: dc_pad_connect, disconnect: dc_pad_disconnect, + reply: dc_pad_callback, + vblank: maple_getcond_vblank_callback, }; diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maple_keyb.c ./drivers/char/maple_keyb.c --- ../linux-sh-orig/drivers/char/maple_keyb.c Thu Dec 6 07:23:03 2001 +++ ./drivers/char/maple_keyb.c Wed Jan 16 00:44:16 2002 @@ -80,16 +80,14 @@ } -static void dc_kbd_callback(struct mapleq *mq) +static void dc_kbd_callback(struct maple_driver_data *data) { - struct maple_device *mapledev = mq->dev; - struct dc_kbd *kbd = mapledev->private_data; - unsigned long *buf = mq->recvbuf; - - if (buf[1] == mapledev->function) { - memcpy(kbd->new, buf+2, 8); - dc_scan_kbd(kbd); - } + struct mapleq *mq=& data->mq; + struct dc_kbd *kbd = data->private_data; + unsigned long *buf = (unsigned long *)mq->recvbuf; + + memcpy(kbd->new, buf+2, 8); + dc_scan_kbd(kbd); } @@ -108,17 +106,17 @@ } -static int dc_kbd_connect(struct maple_device *dev) +static int dc_kbd_connect(struct maple_driver_data *d) { int i; - unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); + unsigned long data = d->function_data; struct dc_kbd *kbd; if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL))) return -1; memset(kbd, 0, sizeof(struct dc_kbd)); - dev->private_data = kbd; + d->private_data = kbd; kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); @@ -131,13 +129,11 @@ kbd->dev.close = dc_kbd_close; kbd->dev.event = NULL; - kbd->dev.name = dev->product_name; + kbd->dev.name = d->dev->product_name; kbd->dev.idbus = BUS_MAPLE; input_register_device(&kbd->dev); - maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD); - printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n", kbd->dev.number, data, kbd->dev.name); @@ -147,9 +143,9 @@ } -static void dc_kbd_disconnect(struct maple_device *dev) +static void dc_kbd_disconnect(struct maple_driver_data *d) { - struct dc_kbd *kbd = dev->private_data; + struct dc_kbd *kbd = d->private_data; input_unregister_device(&kbd->dev); @@ -164,6 +160,8 @@ name: "Dreamcast keyboard", connect: dc_kbd_connect, disconnect: dc_kbd_disconnect, + reply: dc_kbd_callback, + vblank: maple_getcond_vblank_callback, }; diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maple_lg.c ./drivers/char/maple_lg.c --- ../linux-sh-orig/drivers/char/maple_lg.c Wed Dec 31 16:00:00 1969 +++ ./drivers/char/maple_lg.c Tue Jan 22 11:39:07 2002 @@ -0,0 +1,271 @@ +/* + * $Id: maple_lg.c,v 1.1.1.1 2001/10/15 20:44:57 hubbe Exp $ + * SEGA Dreamcast light gun driver + * Based on drivers/maple/maplemouse.c + * + * Written by Fredrik Hubinette <hu...@hu...>, 2002 + * + * You may want to download xguncalibrate from + * http://fredrik.hubbe.net/xguncalibrate.tar.gz to + * calibrate your Lightgun. + * + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/maple.h> +#include <linux/interrupt.h> + +#include <asm/dc_sysasic.h> + +MODULE_AUTHOR("Fredrik Hubinette <hu...@hu...>"); +MODULE_DESCRIPTION("SEGA Dreamcast light gun driver"); + + +#ifdef CONFIG_MAPLE_LG_DEBUG +# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +# define DPRINTK(fmt, args...) +#endif + +/* outx = inx * mult - sub */ +struct dc_lg_axis_param { + int mult; + int sub; + int max; +}; + +struct dc_lightgun { + struct maple_driver_data *data; + struct input_dev dev; + int open; + struct mapleq gun; + struct dc_lg_axis_param xparam, yparam; +}; + + +static void dc_lightgun_callback(struct maple_driver_data *data) +{ + unsigned short buttons; + struct mapleq *mq=&data->mq; + struct dc_lightgun *lg = data->private_data; + struct input_dev *dev = &lg->dev; + unsigned char *res = mq->recvbuf; + + buttons = ~*(unsigned short *)(res+8); + + input_report_key(dev, BTN_LEFT, buttons&4); + input_report_key(dev, BTN_MIDDLE, buttons&8); + input_report_key(dev, BTN_RIGHT, buttons&2); + + input_report_key(dev, KEY_UP, buttons&0x0010); + input_report_key(dev, KEY_DOWN, buttons&0x0020); + input_report_key(dev, KEY_LEFT, buttons&0x0040); + input_report_key(dev, KEY_RIGHT,buttons&0x0080); + +} + + +static void dc_lightgun_pos_callback(void *privdata, int x, int y) +{ + struct dc_lightgun *lg = (struct dc_lightgun *) privdata; +/* printk("\033\r Lightgun: %04x %04x \n",x,y); + printk(" Lightgun: %04x %04x \n",x,y); */ + + x=x * lg->xparam.mult - lg->xparam.sub; + if(x<0) x=0; + if(x >= lg->xparam.max) x = lg->xparam.max -1; + input_report_abs(& lg->dev, ABS_X, x); + + y=y * lg->yparam.mult - lg->yparam.sub; + if(y<0) y=0; + if(y >= lg->yparam.max) y = lg->yparam.max -1; + input_report_abs(& lg->dev, ABS_Y, y); +} + + +static int dc_lightgun_open(struct input_dev *dev) +{ + struct dc_lightgun *lg = dev->private; + if( lg->open++ == 0) { + /* Enable lightgun functionality */ + + maple_set_gunmode(lg->data->dev->port, + dc_lightgun_pos_callback, lg); + } + return 0; +} + +static void dc_lightgun_close(struct input_dev *dev) +{ + struct dc_lightgun *lg = dev->private; + if( --lg->open == 0) { + maple_set_gunmode(lg->data->dev->port, 0, 0); + } +} + + +void dc_lightgun_vblank_callback(struct maple_driver_data *data) +{ + if( data->mq.done ) + { + data->mq.command = MAPLE_COMMAND_GETCOND; + data->mq.length = 1; + ((unsigned long *)data->mq.recvbuf)[0] = MAPLE_FUNC_CONTROLLER; + data->mq.sendbuf = data->mq.recvbuf; + + DPRINTK("queueing GETCOND for %d,%d,%x (%s)\n", + data->mq.port, + data->mq.unit, + data->driver->function, + data->driver->name); + + maple_add_packet(& data->mq ); + + } +} + +static int dc_lightgun_event(struct input_dev *dev, + unsigned int type, + unsigned int code, + int value) +{ + struct dc_lightgun *lg = dev->private; + struct dc_lg_axis_param *tmp; + +#if 0 + printk(KERN_DEBUG " LGEV: %d %d %d\n",type,code,value); +#endif + + if(type != EV_FF) return -1; + switch(code & 0xf0) + { + default: return -2; + case 0: tmp = & lg->xparam; break; + case 1: tmp = & lg->yparam; break; + } + + switch(code & 0xf) + { + default: return -3; + case 0: tmp->mult=value; break; + case 1: tmp->sub=value; break; + case 2: tmp->max=value; break; + } + return 0; +} + + +static int dc_lightgun_connect(struct maple_driver_data *d) +{ + unsigned long data = d->function_data; + struct dc_lightgun *lg; + + if (!(lg = kmalloc(sizeof(struct dc_lightgun), GFP_KERNEL))) + return -1; + memset(lg, 0, sizeof(struct dc_lightgun)); + + lg->data=d; + + lg->xparam.mult=1; + lg->xparam.sub =0xc8; + lg->xparam.max =640; + + lg->yparam.mult=1; + lg->yparam.sub =0x35; + lg->yparam.max =480; + + + d->private_data = lg; + + lg->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF); + lg->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + lg->dev.keybit[LONG(KEY_UP)] |= BIT(KEY_UP); + lg->dev.keybit[LONG(KEY_DOWN)] |= BIT(KEY_DOWN); + lg->dev.keybit[LONG(KEY_LEFT)] |= BIT(KEY_LEFT); + lg->dev.keybit[LONG(KEY_RIGHT)] |= BIT(KEY_RIGHT); + + lg->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y); + + /* FIXME: what should the max values really be? */ + lg->dev.absmax[ABS_X] = 640; + lg->dev.absmin[ABS_X] = 0; + lg->dev.absfuzz[ABS_X]= 20; + lg->dev.absflat[ABS_X]= 0; + + lg->dev.absmax[ABS_Y] = 480; + lg->dev.absmin[ABS_Y] = 0; + lg->dev.absfuzz[ABS_Y]= 20; + lg->dev.absflat[ABS_Y]= 0; + + lg->dev.private = lg; + lg->dev.open = dc_lightgun_open; + lg->dev.close = dc_lightgun_close; + lg->dev.event = dc_lightgun_event; + + lg->dev.name = d->dev->product_name; + lg->dev.idbus = BUS_MAPLE; + + + input_register_device(&lg->dev); + + printk(KERN_INFO "input%d: lightgun(0x%lx): %s\n", + lg->dev.number, data, lg->dev.name); + + MOD_INC_USE_COUNT; + + return 0; +} + + +static void dc_lightgun_disconnect(struct maple_driver_data *d) +{ + struct dc_lightgun *lg = d->private_data; + + while( maple_del_packet( & lg->gun) < 0) + /* yield */; + + input_unregister_device(&lg->dev); + + kfree(lg); + + MOD_DEC_USE_COUNT; +} + + +static struct maple_driver dc_lightgun_driver = { + function: MAPLE_FUNC_LIGHTGUN | MAPLE_FUNC_CONTROLLER, + name: "Dreamcast light gun", + connect: dc_lightgun_connect, + disconnect: dc_lightgun_disconnect, + reply: dc_lightgun_callback, + vblank: dc_lightgun_vblank_callback, +}; + + +static int __init dc_lightgun_init(void) +{ + + maple_register_driver(&dc_lightgun_driver); + return 0; +} + + +static void __exit dc_lightgun_exit(void) +{ + maple_unregister_driver(&dc_lightgun_driver); +} + + +module_init(dc_lightgun_init); +module_exit(dc_lightgun_exit); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maplemouse.c ./drivers/char/maplemouse.c --- ../linux-sh-orig/drivers/char/maplemouse.c Mon Oct 15 13:44:57 2001 +++ ./drivers/char/maplemouse.c Tue Jan 15 05:18:22 2002 @@ -21,11 +21,11 @@ }; -static void dc_mouse_callback(struct mapleq *mq) +static void dc_mouse_callback(struct maple_driver_data *data) { int buttons, relx, rely, relz; - struct maple_device *mapledev = mq->dev; - struct dc_mouse *mouse = mapledev->private_data; + struct mapleq *mq=& data->mq; + struct dc_mouse *mouse = data->private_data; struct input_dev *dev = &mouse->dev; unsigned char *res = mq->recvbuf; @@ -58,16 +58,16 @@ } -static int dc_mouse_connect(struct maple_device *dev) +static int dc_mouse_connect(struct maple_driver_data *d) { - unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); + unsigned long data = d->function_data; struct dc_mouse *mouse; if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) return -1; memset(mouse, 0, sizeof(struct dc_mouse)); - dev->private_data = mouse; + d->private_data = mouse; mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); @@ -78,13 +78,11 @@ mouse->dev.close = dc_mouse_close; mouse->dev.event = NULL; - mouse->dev.name = dev->product_name; + mouse->dev.name = d->dev->product_name; mouse->dev.idbus = BUS_MAPLE; input_register_device(&mouse->dev); - maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); - printk(KERN_INFO "input%d: mouse(0x%lx): %s\n", mouse->dev.number, data, mouse->dev.name); @@ -94,9 +92,9 @@ } -static void dc_mouse_disconnect(struct maple_device *dev) +static void dc_mouse_disconnect(struct maple_driver_data *d) { - struct dc_mouse *mouse = dev->private_data; + struct dc_mouse *mouse = d->private_data; input_unregister_device(&mouse->dev); @@ -111,6 +109,8 @@ name: "Dreamcast mouse", connect: dc_mouse_connect, disconnect: dc_mouse_disconnect, + reply: dc_mouse_callback, + vblank: maple_getcond_vblank_callback, }; diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/maple/maple.c ./drivers/maple/maple.c --- ../linux-sh-orig/drivers/maple/maple.c Mon Oct 15 13:44:59 2001 +++ ./drivers/maple/maple.c Tue Jan 22 11:20:04 2002 @@ -17,10 +17,43 @@ #include <asm/page.h> #include <asm/io.h> +#include <asm/dc_sysasic.h> #include <linux/maple.h> +/* + * Significantly rewritten by Fredrik Hubinette <hu...@hu...> 2002 + */ + +#if 0 #define DPRINTK(format, args...) printk(KERN_DEBUG format,##args) +#else +#define DPRINTK(format, args...) +#endif + +#if 0 +#define POLL_DPRINTK(format, args...) printk(KERN_INFO format,##args) +#else +#define POLL_DPRINTK(format, args...) +#endif + +#if 0 +#define IRQ_DPRINTK(format, args...) printk(KERN_INFO format,##args) +#else +#define IRQ_DPRINTK(format, args...) +#endif + +#if 0 +#define SETUP_DPRINTK(format, args...) printk(KERN_DEBUG format,##args) +#else +#define SETUP_DPRINTK(format, args...) +#endif + +#if 0 +#define DCOLOR(X) ((*(unsigned long *)0xa05f8040)=(X)) +#else +#define DCOLOR(X) +#endif #define MAPLE_PORTS 4 #define MAPLE_SCANHZ (HZ/100) @@ -31,76 +64,74 @@ #define MAPLE_DMA_SIZE (1<<MAPLE_DMA_ORDER) #define MAPLE_DMA_PAGES ((MAPLE_DMA_ORDER > PAGE_SHIFT) ? MAPLE_DMA_ORDER-PAGE_SHIFT : 0) -static LIST_HEAD(maple_dev_list); -static LIST_HEAD(maple_driver_list); -static LIST_HEAD(maple_waitq); -static LIST_HEAD(maple_sentq); -static DECLARE_WAIT_QUEUE_HEAD(kmapled_wait); -static DECLARE_COMPLETION(kmapled_exited); -static int kmapled_pid = 0; +/* Maple units: + * 0 = primary + * 1-5 = secondaries + */ +static int unitcodes[MAPLE_MAX_UNITS] = { 32, 1, 2, 4, 8, 16 }; + +static struct maple_port ports[MAPLE_PORTS]; +static int maple_active_guns; +static int maple_gunport; +static int maple_active_gunport; -struct timer_list maple_timer; +/*** Heleper functions ***/ -unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; -unsigned long maple_pnp_time; +static void maple_detect_callback(struct mapleq *); -static struct maple_driver maple_dummy_driver = { - function: 0, - name: "Dummy", -}; - -static void maple_dump_devinfo(struct maple_devinfo *devinfo) +static int maple_dma_done(void) { - DPRINTK(" function: 0x%08x\n", be32_to_cpu(devinfo->function)); - DPRINTK(" data[0]: 0x%08x\n", be32_to_cpu(devinfo->function_data[0])); - DPRINTK(" data[1]: 0x%08x\n", be32_to_cpu(devinfo->function_data[1])); - DPRINTK(" data[2]: 0x%08x\n", be32_to_cpu(devinfo->function_data[2])); - DPRINTK(" area code: %d\n", devinfo->area_code); - DPRINTK(" direction: %d\n", devinfo->connector_direction); - DPRINTK(" name: \"%-30.30s\"\n", devinfo->product_name); - DPRINTK(" license: \"%-60.60s\"\n", devinfo->product_license); - DPRINTK(" stanby power: %d\n", le16_to_cpu(devinfo->standby_power)); - DPRINTK(" max power: %d\n", le16_to_cpu(devinfo->max_power)); + return (ctrl_inl(MAPLE_STATE) & 1) == 0; } -void maple_register_driver(struct maple_driver *driver) -{ - struct list_head *lh = (void *)driver; - list_add(lh, &maple_driver_list); - - MOD_INC_USE_COUNT; +#define MCP(P,U) do{ \ + if( !(ports[(P)].dev[(U)]) != \ + !(ports[(P)].known_units & unitcodes[(U)]) ) { \ + unsigned long flags; \ + printk(KERN_INFO "MF: %d (%d,%d) u=%x\n",__LINE__,(P),(U),ports[(P)].known_units); \ + save_and_cli(flags); \ + while(1); \ + } \ +}while(0) + +#define CHECKALLP() do{ \ + int P,U; \ + for(P=0;P<4;P++) for(U=0;U<MAPLE_MAX_UNITS;U++) MCP(P,U); \ +}while(0) + + +/*** Low-level maple functions *** + * + * This section only handles data transfers and callbacks, + * it does not handle any devices, auto-detection or + * timing. See the next section for that. + */ - printk(KERN_INFO "maple: registered driver: %s (function 0x%lx)\n", - driver->name, driver->function); -} +unsigned long *maple_sendbuf, *maple_sendptr; +static LIST_HEAD(maple_waitq); +static LIST_HEAD(maple_sentq); -void maple_unregister_driver(struct maple_driver *driver) +void maple_init_mq(struct mapleq *mq) { - struct list_head *lh = (void *)driver; - list_del(lh); - - MOD_DEC_USE_COUNT; + unsigned long buf; - printk(KERN_INFO "maple: unregistered driver: %s (function 0x%lx)\n", - driver->name, driver->function); + mq->done = 1; + buf = (unsigned long)mq->buf; + buf = (buf + 31) & ~31; + mq->recvbuf = (void *)P2SEGADDR(buf); } - static struct mapleq *maple_allocq(struct maple_device *dev) { - unsigned long buf; struct mapleq *mq; mq = kmalloc(sizeof(*mq), GFP_KERNEL); if (!mq) return NULL; - mq->dev = dev; - buf = (unsigned long)mq->buf; - buf = (buf + 31) & ~31; - mq->recvbuf = (void *)P2SEGADDR(buf); + maple_init_mq(mq); return mq; } @@ -114,93 +145,235 @@ kfree(mq); } +static unsigned long last_gun_data; -static struct maple_device *maple_alloc_dev(int port, int unit) +static void maple_dma_irq(int irq, void *dev, struct pt_regs *fp) { - struct maple_device *dev; + struct mapleq *mq; + struct list_head *lh, tmph; - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; + DCOLOR(0x0000ff); + IRQ_DPRINTK("MAPLE DMA IRQ BEGIN\n"); - memset(dev, 0, sizeof(*dev)); + POLL_DPRINTK("Maple DMA finished %d, %d.\n", + list_empty(&maple_sentq), !maple_dma_done()); - dev->port = port; - dev->unit = unit; - dev->mq = maple_allocq(dev); + if (list_empty(&maple_sentq) || !maple_dma_done()) return; - if (!dev->mq) { - kfree(dev); - return NULL; +#if 1 + if(maple_active_gunport >= 0) { + int x,y; + unsigned long tmp=*(unsigned long *)0xA05F80C4; + if(last_gun_data != tmp) { + y=tmp>>16; + x=tmp&0xffff; + POLL_DPRINTK("Maple gunport reply for port %d: " + "%04x %04x\n", + maple_active_gunport, + x,y); + ports[maple_active_gunport].gunmode( + ports[maple_active_gunport].gunmode_data,x,y); + maple_active_gunport=-1; + } } +#endif - return dev; + list_for_each(lh, &maple_sentq) { + tmph = *lh; + + mq = (struct mapleq *)lh; + mq->done=1; + POLL_DPRINTK("Maple reply (%d,%d) cmd=%d => %d\n", + mq->port, mq->unit, + mq->command, mq->recvbuf[0]); + + if(mq->recvbuf[0] == MAPLE_RESPONSE_NONE) + maple_detect_callback(mq); + else if(mq->callback) + mq->callback(mq); + + lh = &tmph; + + } + + INIT_LIST_HEAD(&maple_sentq); + maple_send(); + + IRQ_DPRINTK("MAPLE DMA IRQ END\n"); } -static void maple_free_dev(struct maple_device *dev) + + +/* Call this after one or more calls to maple_add_packet */ +/* FIXME: implement scheduling */ +static void low_maple_send(int vblank) { - if (!dev) + int i; + int maple_packets; + struct mapleq *mq; + struct list_head *lh, tmph; + unsigned long *maple_lastptr; + unsigned long flags; + + /* Major-league dodgy */ + save_and_cli(flags); + + if (!list_empty(&maple_sentq) || + (maple_active_gunport != -1 && !vblank) ) { + restore_flags(flags); return; + } - if (dev->mq) - maple_freeq(dev->mq); + if (list_empty(&maple_waitq) || !maple_dma_done()) { + restore_flags(flags); + return; + } - kfree(dev); -} + maple_packets = 0; + maple_sendptr = maple_lastptr = maple_sendbuf; + maple_active_gunport=-1; + list_for_each(lh, &maple_waitq) { + int port, unit, from, to, len; + unsigned long *lsendbuf; -static void maple_register_dev(struct maple_device *dev) -{ - struct list_head *lh = (void *)dev; - list_add_tail(lh, &maple_dev_list); -} + mq = (struct mapleq *)lh; + + port = mq->port; + unit = mq->unit; + len = mq->length & 0xff; + + if(ports[port].dev[unit] && + ports[port].dev[unit]->lock) + { + if(vblank) ports[port].dev[unit]->lock--; + continue; + } + tmph = *lh; -static void maple_unregister_dev(struct maple_device *dev) -{ - struct list_head *lh = (void *)dev; - list_del(lh); -} + list_del(lh); + list_add_tail(lh, &maple_sentq); + lsendbuf = mq->sendbuf; + + POLL_DPRINTK("Maple sending (%d,%d) cmd=%d len=%d\n", + mq->port, mq->unit, mq->command, len); + + from = port<<6; + to = (port<<6) | unitcodes[unit]; + + maple_lastptr = maple_sendptr; + + *maple_sendptr++ = (port<<16) | len; + *maple_sendptr++ = PHYSADDR(mq->recvbuf); + *maple_sendptr++ = mq->command | (to<<8) | + (from<<16) | (len<<24); + +#if 1 + while (len-->0) { + *maple_sendptr++ = *lsendbuf++; + } +#else + if(len > 0) + { + memcpy((char *)maple_sendptr, + (char *)lsendbuf, + sizeof(*maple_sendptr) * (len - 1)); + + maple_sendptr+=len; + } +#endif + if(mq->length & MAPLEQ_LENGTH_GUN) { +/* printk("Setting GUN MODE!\n"); */ + maple_lastptr = maple_sendptr; + *maple_sendptr++ = (port<<16) | 0x200; + maple_active_gunport = port; + break; + } + + lh = &tmph; + if (maple_packets++ > MAPLE_MAXPACKETS) + break; + } + +#if 0 + *maple_lastptr |= 0x80000000; /* Set 'last' bit */ +#else + switch(maple_active_guns) + { + case 0: + maple_active_gunport=-1; + *maple_lastptr |= 0x80000000; /* Set 'last' bit */ + break; + + default: + do { + if(++maple_gunport == MAPLE_PORTS) + maple_gunport=0; + } while( ! ports[maple_gunport].gunmode); + + /* FALL THROUGH */ + case 1: + maple_active_gunport=maple_gunport; + *maple_sendptr++ = (maple_gunport <<16) | 0x80000200; + POLL_DPRINTK("Active gunport = %d\n", + maple_active_gunport); + } +#endif + + if (maple_packets>0) { + for (i=0; i<(1<<MAPLE_DMA_PAGES); i++) + dma_cache_wback_inv(maple_sendbuf+i*PAGE_SIZE, PAGE_SIZE); + POLL_DPRINTK("Maple, starting DMA packets=%d\n",maple_packets); + ctrl_outl(1, MAPLE_STATE); + DCOLOR(0x00ffff); + } + + restore_flags(flags); -void maple_getcond_callback(struct maple_device *dev, - void (*callback)(struct mapleq *mq), - unsigned long interval, unsigned long function) -{ - dev->callback = callback; - dev->interval = interval; - dev->function = cpu_to_be32(function); - dev->when = 0; } +void maple_send(void) +{ + low_maple_send(0); +} int maple_add_packet(struct mapleq *mq) { + int ret=0; unsigned long flags; save_and_cli(flags); - list_add((struct list_head *)mq, &maple_waitq); + if(mq->done) { + mq->done=0; + list_add_tail((struct list_head *)mq, &maple_waitq); + }else{ + DPRINTK("Maple, rejecting package (to %d,%d, cmd=%d)\n", + mq->port, mq->unit, + mq->command); + ret=-EINPROGRESS; + } restore_flags(flags); - return 0; + return ret; } - int maple_del_packet(struct mapleq *mq) { struct list_head *lh; unsigned long flags; save_and_cli(flags); - + list_for_each(lh, &maple_sentq) { if (mq == (struct mapleq *)lh) { restore_flags(flags); - return -1; + return -EBUSY; } } - + list_for_each(lh, &maple_waitq) { if (mq == (struct mapleq *)lh) list_del(lh); @@ -211,243 +384,375 @@ return 0; } - -static int maple_dma_done(void) +void maple_set_gunmode(int port, void (cb)(void *,int,int),void *data) { - return (ctrl_inl(MAPLE_STATE) & 1) == 0; -} +#if 1 + unsigned long flags; + save_and_cli(flags); -static void maple_attach_driver(struct maple_device *dev) -{ - struct list_head *lh; - struct maple_driver *driver; - unsigned long function; - char *p, *recvbuf = dev->mq->recvbuf; + if( (!ports[port].gunmode) ^ (!cb) ) { + maple_active_guns += (cb ? 1 : -1); + if(cb) maple_gunport=port; + } - memcpy(&dev->devinfo, recvbuf+4, sizeof(dev->devinfo)); - memcpy(dev->product_name, dev->devinfo.product_name, 30); - memcpy(dev->product_license, dev->devinfo.product_license, 60); - dev->product_name[30] = 0; - dev->product_license[60] = 0; + ports[port].gunmode = cb; + ports[port].gunmode_data = data; - for (p=dev->product_name+29; dev->product_name<=p; p--) - if (*p==' ') *p = 0; else break; - - for (p=dev->product_license+59; dev->product_license<=p; p--) - if (*p==' ') *p = 0; else break; - function = be32_to_cpu(dev->devinfo.function); + restore_flags(flags); - printk(KERN_INFO "maple(%d,%d): Connected(function 0x%lx)\n", - dev->port, dev->unit, function); + printk("Setting gunmode for port %d to %p(%p) (active guns = %d)\n",port,cb,data,maple_active_guns); +#endif +} - list_for_each(lh, &maple_driver_list) { - driver = (struct maple_driver *)lh; - if (function & driver->function) { - if (!driver->connect(dev)) { - dev->driver = driver; - break; - } - } +static int init_maple_low(void) +{ + int p, u; + + printk(KERN_INFO "SEGA Dreamcast MAPLE Bus drivers\n"); + + /* Allocate DMA buffer */ + maple_sendbuf = (void *)__get_dma_pages(GFP_KERNEL, MAPLE_DMA_PAGES); + if (maple_sendbuf == NULL) + return -ENOMEM; + memset(maple_sendbuf, 0, MAPLE_DMA_SIZE); + + for(p=0;p<MAPLE_PORTS;p++) + { + ports[p].port=p; + ports[p].known_units=0; + ports[p].units=0; + ports[p].gunmode=0; + + for(u=0;u<MAPLE_MAX_UNITS;u++) + ports[p].dev[u]=NULL; } + CHECKALLP(); - if (dev->driver==NULL) { - printk(KERN_INFO "maple(%d,%d): No proper driver.\n", - dev->port, dev->unit); - maple_dump_devinfo(&dev->devinfo); - dev->driver = &maple_dummy_driver; + /* Initialize hardware */ + ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); + ctrl_outl(0, MAPLE_RESET2); + ctrl_outl(MAPLE_2MBPS|MAPLE_TIMEOUT(50000), MAPLE_SPEED); + ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); + ctrl_outl(1, MAPLE_ENABLE); + + + if (request_irq(HW_EVENT_MAPLE_DMA, maple_dma_irq, 0, + "Maple BUS DMA", 0)) { + DPRINTK("couldn't register DMA int\n"); + goto cleanup; } + return 0; + cleanup: + printk(KERN_INFO "maple: Register failed\n"); + return -ENOMEM; } - -static void maple_detach_driver(struct maple_device *dev) +static void exit_maple_low(void) { - printk(KERN_INFO "maple(%d,%d): Disconnected\n", - dev->port, dev->unit); + free_irq(HW_EVENT_MAPLE_DMA, 0); + + /* twiddle */ + while(!maple_dma_done()); - dev->driver->disconnect(dev); - dev->driver = NULL; + if (maple_sendbuf) + free_pages((unsigned long)maple_sendbuf, MAPLE_DMA_PAGES); } -static void maple_dma_irq(void) + +/*** Maple Device interface *** + * + * This section provides the interface for individual + * drivers and handles device state polling and auto-detection. + */ + +static LIST_HEAD(maple_data_list); +static LIST_HEAD(maple_driver_list); +static DECLARE_WAIT_QUEUE_HEAD(kmapled_wait); +static DECLARE_COMPLETION(kmapled_exited); +static int kmapled_pid = 0; + + +static int maple_detector_counter; +static struct mapleq maple_detector; +static int detector_new_dev; + + +static void maple_dump_devinfo(struct maple_devinfo *devinfo) { - struct mapleq *mq; - struct maple_device *dev; - struct list_head *lh, tmph; - char *recvbuf; - int code, need_wakeup = 0; + DPRINTK(" function: 0x%08x\n", be32_to_cpu(devinfo->function)); + DPRINTK(" data[0]: 0x%08x\n", be32_to_cpu(devinfo->function_data[0])); + DPRINTK(" data[1]: 0x%08x\n", be32_to_cpu(devinfo->function_data[1])); + DPRINTK(" data[2]: 0x%08x\n", be32_to_cpu(devinfo->function_data[2])); + DPRINTK(" area code: %d\n", devinfo->area_code); + DPRINTK(" direction: %d\n", devinfo->connector_direction); + DPRINTK(" name: \"%-30.30s\"\n", devinfo->product_name); + DPRINTK(" license: \"%-60.60s\"\n", devinfo->product_license); + DPRINTK(" stanby power: %d\n", le16_to_cpu(devinfo->standby_power)); + DPRINTK(" max power: %d\n", le16_to_cpu(devinfo->max_power)); +} - if (list_empty(&maple_sentq) || !maple_dma_done()) return; - list_for_each(lh, &maple_sentq) { - mq = (struct mapleq *)lh; - dev = mq->dev; - tmph = *lh; - lh = &tmph; +void maple_register_driver(struct maple_driver *driver) +{ + struct list_head *lh = (void *)driver; + list_add(lh, &maple_driver_list); - recvbuf = mq->recvbuf; - code = recvbuf[0]; + MOD_INC_USE_COUNT; - switch (code) { - case MAPLE_RESPONSE_NONE: - case MAPLE_RESPONSE_DEVINFO: - maple_getcond_callback(dev, NULL, 0, 0); - dev->event = code; - need_wakeup = 1; - break; + printk(KERN_INFO "maple: registered driver: %s (function 0x%lx)\n", + driver->name, driver->function); +} - case MAPLE_RESPONSE_DATATRF: - if (dev->callback) - dev->callback(mq); - break; - default: - break; - } +void maple_unregister_driver(struct maple_driver *driver) +{ + struct list_head *lh = (void *)driver; + list_del(lh); - } - - INIT_LIST_HEAD(&maple_sentq); + MOD_DEC_USE_COUNT; - if (need_wakeup) - wake_up(&kmapled_wait); + printk(KERN_INFO "maple: unregistered driver: %s (function 0x%lx)\n", + driver->name, driver->function); } -static void maple_build_block(struct mapleq *mq) +static struct maple_device *maple_alloc_dev(int port, int unit) { - int port, unit, from, to, len; - unsigned long *lsendbuf = mq->sendbuf; + unsigned long flags; + struct maple_device *dev; - port = mq->dev->port & 3; - unit = mq->dev->unit; - len = mq->length; + SETUP_DPRINTK("Maple, Allocating unit %d, %d\n",port, unit); - from = port<<6; - to = (port<<6) | (unit>0 ? (1<<(unit-1))&0x1f : 0x20); + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; - *maple_lastptr &= 0x7fffffff; - maple_lastptr = maple_sendptr; + memset(dev, 0, sizeof(*dev)); - *maple_sendptr++ = (port<<16) | len | 0x80000000; - *maple_sendptr++ = PHYSADDR(mq->recvbuf); - *maple_sendptr++ = mq->command | (to<<8) | (from<<16) | (len<<24); + dev->port = port; + dev->unit = unit; + dev->lock = 0; - while (len-->0) { - *maple_sendptr++ = *lsendbuf++; - } -} + CHECKALLP(); + save_and_cli(flags); + ports[port].dev[unit]=dev; + ports[port].known_units |= unitcodes[unit]; + restore_flags(flags); -void maple_send(void) -{ - int i; - int maple_packets; - struct mapleq *mq; - struct list_head *lh, tmph; + CHECKALLP(); - if (!list_empty(&maple_sentq)) - return; + return dev; +} - if (list_empty(&maple_waitq) || !maple_dma_done()) - return; - maple_packets = 0; - maple_sendptr = maple_lastptr = maple_sendbuf; +void maple_getcond_vblank_callback(struct maple_driver_data *data) +{ + if( data->mq.done ) + { + data->mq.command = MAPLE_COMMAND_GETCOND; + data->mq.length = 1; + ((unsigned long *)data->mq.recvbuf)[0] = + cpu_to_be32(data->driver->function); + data->mq.sendbuf = data->mq.recvbuf; + + POLL_DPRINTK("queueing GETCOND for %d,%d,%lx (%s)\n", + data->mq.port, + data->mq.unit, + data->driver->function, + data->driver->name); + + maple_add_packet(& data->mq ); + } +} - list_for_each(lh, &maple_waitq) { - mq = (struct mapleq *)lh; - tmph = *lh; - list_del(lh); - list_add(lh, &maple_sentq); - maple_build_block(mq); - lh = &tmph; - if (maple_packets++ > MAPLE_MAXPACKETS) +static void maple_driver_cmd_callback(struct mapleq *mq) +{ + struct maple_driver_data *data=(struct maple_driver_data *)mq->mq_privdata; + if(data->driver->reply) + data->driver->reply(data); +} + +static int maple_connect(struct maple_device *dev, + struct maple_driver *driver) +{ + unsigned long flags, func; + int tmp; + struct maple_driver_data *data; + data = kmalloc(sizeof(*data), GFP_KERNEL); + + if(!data) return -ENOMEM; + data->driver=driver; + data->dev=dev; + data->private_data=0; + + /* Init mq as much as possible */ + maple_init_mq(& data->mq ); + data->mq.mq_privdata=data; + data->mq.port=dev->port; + data->mq.unit=dev->unit; + data->mq.callback=maple_driver_cmd_callback; + + for(func=data->dev->function, tmp=0;;tmp++) { + func &= func-1; + if(data->driver->function > func) break; } + data->function_data=be32_to_cpu(dev->devinfo.function_data[tmp]); + SETUP_DPRINTK("Function data: %lx\n",data->function_data); - if (maple_packets>0) { - for (i=0; i<(1<<MAPLE_DMA_PAGES); i++) - dma_cache_wback_inv(maple_sendbuf+i*PAGE_SIZE, PAGE_SIZE); - ctrl_outl(1, MAPLE_STATE); + if(! driver->connect(data)) { + save_and_cli(flags); + list_add((struct list_head *)data, &maple_data_list); + restore_flags(flags); + return 0; } -} + kfree( data ); + CHECKALLP(); -static void maple_timer_handler(unsigned long dummy) + return 1; +} + +static void maple_attach_driver(int port, int unit) { struct list_head *lh; - struct maple_device *dev; + struct maple_driver *driver; + unsigned long function, bits_left; + char *p, *recvbuf = maple_detector.recvbuf; - /* If DMA is done, dispatch callback functions */ - maple_dma_irq(); + struct maple_device *dev=maple_alloc_dev(port, unit); - /* Do task for each device */ - list_for_each (lh, &maple_dev_list) { - dev = (struct maple_device *)lh; - if (dev->event) - continue; - if (dev->interval>0) { - if (jiffies>dev->when) { - dev->when = jiffies + dev->interval; - dev->mq->command = MAPLE_COMMAND_GETCOND; - dev->mq->sendbuf = &dev->function; - dev->mq->length = 1; - maple_add_packet(dev->mq); + memcpy(&dev->devinfo, recvbuf+4, sizeof(dev->devinfo)); + + memcpy(dev->product_name, dev->devinfo.product_name, 30); + memcpy(dev->product_license, dev->devinfo.product_license, 60); + dev->product_name[30] = 0; + dev->product_license[60] = 0; + + for (p=dev->product_name+29; dev->product_name<=p; p--) + if (*p==' ') *p = 0; else break; + + for (p=dev->product_license+59; dev->product_license<=p; p--) + if (*p==' ') *p = 0; else break; + + function = be32_to_cpu(dev->devinfo.function); + dev->function=function; + + printk(KERN_INFO "maple(%d,%d): Connected(function 0x%lx)\n", + dev->port, dev->unit, function); + + /* First, try all functions of this device */ + for(bits_left=function;bits_left;) { + int best_bits=0; + + /* Find out the maximum number of functions we can get + * from one driver. + */ + list_for_each(lh, &maple_driver_list) { + unsigned long bits, tmp; + + driver = (struct maple_driver *)lh; + if(driver->function & ~bits_left) continue; + tmp=bits_left & driver->function; + bits=0; + while(tmp) + { + tmp = tmp & (tmp -1); + bits++; } + if(bits > best_bits) + best_bits=bits; } - else { - if (jiffies>=maple_pnp_time) { - dev->mq->command = MAPLE_COMMAND_DEVINFO; - dev->mq->length = 0; - maple_add_packet(dev->mq); + + /* Now, first try the best drivers, + * then the second best, etc. + */ + for(;best_bits>0;best_bits--) { + list_for_each(lh, &maple_driver_list) { + unsigned long bits, tmp; + + driver = (struct maple_driver *)lh; + if(driver->function & ~bits_left) continue; + tmp=bits_left & driver->function; + bits=0; + while(tmp) { + tmp = tmp & (tmp -1); + bits++; + } + if(bits == best_bits && + !maple_connect(dev,driver)) { + bits_left=bits_left & + ~driver->function; + goto check_more_bits; + + } } } - } - if (jiffies>=maple_pnp_time) - maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL; - - /* Scan list and build sending block */ - maple_send(); + printk(KERN_INFO "maple(%d,%d): No driver for function(s): %lx.\n", + dev->port, dev->unit, bits_left); + maple_dump_devinfo(&dev->devinfo); + break; - /* Schedule next timer event */ - mod_timer(&maple_timer, jiffies + MAPLE_SCANHZ); + check_more_bits: ; + } + + CHECKALLP(); } -static void maple_pnp_events(void) +static void maple_detach_driver(int port, int unit) { - struct maple_device *dev; - struct list_head *lh; + unsigned long flags; - list_for_each(lh, &maple_dev_list) { + struct list_head *lh, tmph; + struct maple_device *dev=ports[port].dev[unit]; - dev = (struct maple_device *)lh; + if(!dev) + return; - switch(dev->event) { - case MAPLE_RESPONSE_NONE: - if (dev->driver) - maple_detach_driver(dev); - break; + printk(KERN_INFO "maple(%d,%d): Disconnected (%d,%d)\n", + dev->port, dev->unit, port, unit); - case MAPLE_RESPONSE_DEVINFO: - if (!dev->driver) - maple_attach_driver(dev); - break; - } + list_for_each(lh, &maple_data_list) { + struct maple_driver_data *data; + data=(struct maple_driver_data *)lh; + if(data->dev == dev) { + save_and_cli(flags); + tmph=*lh; + list_del(lh); + restore_flags(flags); + + data->driver->disconnect(data); - dev->event = 0; + while( maple_del_packet( & data->mq ) < 0) + /* yield() */; + kfree(lh); + lh=&tmph; + } } + + save_and_cli(flags); + ports[port].dev[unit]=0; + ports[port].known_units &=~ unitcodes[unit]; + restore_flags(flags); + + CHECKALLP(); + + kfree(dev); } + +/* kmapled */ static int kmapled_thread(void *hoge) { lock_kernel(); @@ -457,7 +762,43 @@ strcpy(current->comm, "kmapled"); do { - maple_pnp_events(); + /*Yawn*/ + /* Time to see if there are any new devices */ + + SETUP_DPRINTK("kmapled: *yawn*\n"); + CHECKALLP(); + + int p, u; + + /* maple_detector contains devinfo for a new device ? */ + if(detector_new_dev) { + maple_attach_driver(maple_detector.port, + maple_detector.unit); + detector_new_dev=0; + maple_detector.done=1; /* allow more autodetects */ + } + + for(p=0;p<MAPLE_PORTS;p++) { + if(!(ports[p].known_units & ~ports[p].units)) + continue; + + SETUP_DPRINTK("Maple, port[%d] units=%x known_units=%x\n", + p, + ports[p].units, + ports[p].known_units); + + for(u=0;u<MAPLE_MAX_UNITS;u++) { + if(unitcodes[u] & + ports[p].known_units & + ~ports[p].units) { + maple_detach_driver(p,u); + } + } + } + + CHECKALLP(); + + /* Wait for another event */ interruptible_sleep_on(&kmapled_wait); } while(!signal_pending(current)); @@ -466,30 +807,152 @@ complete_and_exit(&kmapled_exited, 0); } +/* + * If there is an unconfigure device on this port, + * send a DEVINFO request to it. + */ +static int maple_detect_resend(void) +{ + int u,p; + if(! maple_detector.done ) return 0; + + CHECKALLP(); + + p=maple_detector.port; + if(ports[p].units & ~ports[p].known_units) { + for(u=0;u<MAPLE_MAX_UNITS;u++) { + if(unitcodes[u] & + ports[p].units & + ~ports[p].known_units) { + SETUP_DPRINTK("Detector resender, sending devinfo to unit 0 on port %d\n",p); + maple_detector.command=MAPLE_COMMAND_DEVINFO; + maple_detector.port = p; + maple_detector.unit = u; + maple_detector.length = 0; + maple_add_packet(& maple_detector); + return 1; + } + } + } + return 0; +} -static int __init maple_init(void) + +/* + * Autodetect DMA callback. + * 1) Wake up kmapled when we receive a MAPLE_RESPONSE_DEVINFO + * for an unconfigured device. + * 2) Wake up kmapled if devices have been removed. + */ +static void maple_detect_callback(struct mapleq *mq) { - struct maple_device *dev; - int i; + int p=maple_detector.port; + int u=maple_detector.unit; - printk(KERN_INFO "SEGA Dreamcast MAPLE Bus drivers\n"); +/* SETUP_DPRINTK("Detector callback.\n"); */ - /* Allocate DMA buffer */ - maple_sendbuf = (void *)__get_dma_pages(GFP_KERNEL, MAPLE_DMA_PAGES); - if (maple_sendbuf == NULL) - return -ENOMEM; - memset(maple_sendbuf, 0, MAPLE_DMA_SIZE); + CHECKALLP(); - /* Register dummy maple driver */ - maple_register_driver(&maple_dummy_driver); + switch(mq->recvbuf[0]) { + case MAPLE_RESPONSE_DEVINFO: + SETUP_DPRINTK("Detector reply DEVINFO (%d, %d), units = %x.\n",p,u,mq->recvbuf[2] & 0x3f); + if(!u) + ports[p].units = mq->recvbuf[2] & 0x3f; + else + ports[p].units |= unitcodes[u]; + + if(!ports[p].dev[u]) { + maple_detector.done=0; + detector_new_dev=1; + SETUP_DPRINTK("Kmapled, wakie wakie (new device) ... "); + wake_up(&kmapled_wait); + return; + } + break; - /* Register basic port devices */ - for (i=0; i<MAPLE_PORTS; i++) { - dev = maple_alloc_dev(i, 0); - if (!dev) - goto cleanup; - maple_register_dev(dev); + case MAPLE_RESPONSE_NONE: + SETUP_DPRINTK("Detector reply NONE (%d,%d)\n",p,u); + if(!u) + ports[p].units = 0; + else + ports[p].units &=~ unitcodes[u]; + break; + } + maple_detect_resend(); + + /* Devices are gone */ + if(ports[p].known_units & ~ports[p].units) { + SETUP_DPRINTK("Kmapled, wakie wakie (device gone) ... "); + wake_up(&kmapled_wait); } +} + +/* + * Autodetect vertical blank callback + * Check if there are subdevices to configure. + * If not, check if it is time to send a COMMAND_DEVINFO + * to a primary device on a port. + */ +static void maple_detect_vblank(void) +{ + int p; + + CHECKALLP(); + + if(! maple_detector.done ) return; + if( maple_detect_resend() ) return; + + maple_detector_counter++; + if(maple_detector_counter & 0xf) return; + p = (maple_detector_counter >> 4 ) & 3; + + SETUP_DPRINTK("Detector, sending devinfo to unit 0 on port %d\n",p); + + maple_detector.command=MAPLE_COMMAND_DEVINFO; + maple_detector.port = p; + maple_detector.unit = 0; + maple_detector.length = 0; + maple_detector.callback = maple_detect_callback; + maple_add_packet(& maple_detector); +} + +static void maple_vblank_interupt(int irq, void *dev, struct pt_regs *fp) +{ + struct list_head *lh; + struct maple_driver_data *data; + + IRQ_DPRINTK("MAPLE VBLANK IRQ BEGIN\n"); + DCOLOR(0xff00ff); + + /* Dodgyness, damn maple! */ + if(maple_active_gunport >=0) maple_dma_irq(0,0,0); + + + maple_detect_vblank(); + + + /* Do task for each device */ + list_for_each (lh, &maple_data_list) { + data = (struct maple_driver_data *)lh; + if(data->driver->vblank) + data->driver->vblank(data); + } + + /* Scan list and build sending block */ + low_maple_send(1); + + IRQ_DPRINTK("MAPLE VBLANK IRQ END\n"); +} + + +static int __init maple_init(void) +{ + int i; + + if((i=init_maple_low())) + return i; + + maple_init_mq(& maple_detector ); /* Start kernel thread */ kmapled_pid = kernel_thread(kmapled_thread, NULL, @@ -497,21 +960,11 @@ if (kmapled_pid==0) goto cleanup; - /* Start to scan ports */ - maple_pnp_time = 0; - - /* Initialize hardware */ - ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); - ctrl_outl(0, MAPLE_RESET2); - ctrl_outl(MAPLE_2MBPS|MAPLE_TIMEOUT(50000), MAPLE_SPEED); - ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); - ctrl_outl(1, MAPLE_ENABLE); - - /* Initialize timer */ - init_timer(&maple_timer); - maple_timer.expires = jiffies + MAPLE_SCANHZ; - maple_timer.function = maple_timer_handler; - add_timer(&maple_timer); + if (request_irq(HW_EVENT_VSYNC, maple_vblank_interupt, SA_SHIRQ, + "Maple BUS", 0)) { + DPRINTK("couldn't register VBL int\n"); + goto cleanup; + } return 0; @@ -526,24 +979,29 @@ { /* XXX: Must do proper clean-up */ + free_irq(HW_EVENT_VSYNC, 0); + kill_proc(kmapled_pid, SIGTERM, 1); wait_for_completion(&kmapled_exited); - if (maple_sendbuf) - free_pages((unsigned long)maple_sendbuf, MAPLE_DMA_PAGES); - - del_timer(&maple_timer); + exit_maple_low(); } module_init(maple_init); module_exit(maple_exit); +EXPORT_SYMBOL(maple_init_mq); +EXPORT_SYMBOL(maple_allocq); +EXPORT_SYMBOL(maple_freeq); EXPORT_SYMBOL(maple_add_packet); EXPORT_SYMBOL(maple_del_packet); +EXPORT_SYMBOL(maple_send); +EXPORT_SYMBOL(maple_set_gunmode); + EXPORT_SYMBOL(maple_register_driver); EXPORT_SYMBOL(maple_unregister_driver); -EXPORT_SYMBOL(maple_getcond_callback); +EXPORT_SYMBOL(maple_getcond_vblank_callback); /* * Local variables: diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/video/pvr2fb.c ./drivers/video/pvr2fb.c --- ../linux-sh-orig/drivers/video/pvr2fb.c Mon Oct 15 13:45:05 2001 +++ ./drivers/video/pvr2fb.c Tue Jan 8 01:17:00 2002 @@ -1051,7 +1051,7 @@ return -EINVAL; } - if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, 0, + if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, SA_SHIRQ, "pvr2 VBL handler", ¤tpar)) { DPRINTK("couldn't register VBL int\n"); return -EBUSY; diff -U3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/include/linux/maple.h ./include/linux/maple.h --- ../linux-sh-orig/include/linux/maple.h Mon Oct 15 13:45:12 2001 +++ ./include/linux/maple.h Tue Jan 22 11:36:22 2002 @@ -64,7 +64,6 @@ #define MAPLE_FUNC_PURUPURU 0x100 #define MAPLE_FUNC_MOUSE 0x200 - struct maple_devinfo { unsigned long function; /* big endian! */ unsigned long function_data[3]; /* big endian! */ @@ -80,48 +79,81 @@ struct maple_driver; struct maple_device; + +#define MAPLEQ_LENGTH_GUN (1<<9) + struct mapleq { struct list_head list; - struct maple_device *dev; - void *sendbuf, *recvbuf; - unsigned char command, length; + void *mq_privdata; + void *sendbuf; + char *recvbuf; /* points to ->buf, but 32-byte aligned */ + + unsigned char port, unit; + unsigned char command; + unsigned char done; + int length; + + void (*callback)(struct mapleq *mq); unsigned char buf[1024+32]; }; struct maple_device { - struct list_head list; - struct maple_driver *driver; - struct mapleq *mq; - void *private_data; - void (*callback)(struct mapleq *mq); - unsigned long when, interval, function; - int event; + unsigned char port; + unsigned char unit; + short lock; + + unsigned long function; /* Little endian */ struct maple_devinfo devinfo; - unsigned char port, unit; + char product_name[32]; char product_license[64]; }; +/* Max devices per port */ +#define MAPLE_MAX_UNITS 6 + +struct maple_port { + unsigned char port; + unsigned char known_units; /* connected units */ + unsigned char units; /* units to connect/disconnect */ + void (*gunmode)(void *,int,int); + void *gunmode_data; + struct maple_device *dev[MAPLE_MAX_UNITS]; +}; + +struct maple_driver_data { + struct list_head list; + struct maple_driver *driver; + struct maple_device *dev; + void *private_data; + unsigned long function_data; + struct mapleq mq; +}; struct maple_driver { struct list_head list; - unsigned long function; + unsigned long function; /* One or more bits */ const char *name; - int (*connect)(struct maple_device *dev); - void (*disconnect)(struct maple_device *dev); + + void (*vblank)(struct maple_driver_data *dev); + void (*reply)(struct maple_driver_data *dev); + int (*connect)(struct maple_driver_data *dev); + void (*disconnect)(struct maple_driver_data *dev); }; +/* Prototypes begin here */ +void maple_send(void); int maple_add_packet(struct mapleq *mq); int maple_del_packet(struct mapleq *mq); - void maple_register_driver(struct maple_driver *driver); void maple_unregister_driver(struct maple_driver *driver); +void maple_getcond_vblank_callback(struct maple_driver_data *data); +void maple_set_gunmode(int, void (*)(void *,int,int), void *); +void maple_init_mq(struct mapleq *mq); +/* Prototypes end here */ -void maple_getcond_callback(struct maple_device *dev, - void (*callback)(struct mapleq *mq), - unsigned long interval, unsigned long function); /* * Local variables: |
From: Fredrik H. <hu...@hu...> - 2002-01-22 19:41:36
|
This is a patch against the current (Jan 22 2002) linuxsh tree. Some of the highlights include: o All Maple devices are probed o Lightgun Support included o Drivers may support one or more functions o Multiple drivers per device possible (But only one per function) o Uses DMA interrupt when possible My next goal is to impelement a VMU MTD or block device. I have also written a small tool for calibrating the Lightgun device(s) which can be downloaded from: http://fredrik.hubbe.net/xguncalibrate.tar.gz Please let me know if there are any problems with this patch. -----------------------------8<-------------------------------- diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/arch/sh/config.in ./arch/sh/config.in *** ../linux-sh-orig/arch/sh/config.in Fri Nov 2 16:52:47 2001 --- ./arch/sh/config.in Mon Jan 7 22:22:18 2002 *************** *** 319,324 **** --- 319,325 ---- if [ "$CONFIG_INPUT" != "n" ]; then dep_tristate ' Maple Bus keyboard support' CONFIG_MAPLE_KEYBOARD $CONFIG_INPUT dep_tristate ' Maple Bus mouse support' CONFIG_MAPLE_MOUSE $CONFIG_INPUT + dep_tristate ' Maple Light Gun support' CONFIG_MAPLE_LIGHTGUN $CONFIG_INPUT else comment 'Input core support is required for Maple input peripherals' fi diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/Makefile ./drivers/char/Makefile *** ../linux-sh-orig/drivers/char/Makefile Fri Nov 30 15:03:52 2001 --- ./drivers/char/Makefile Mon Jan 7 22:19:27 2002 *************** *** 178,183 **** --- 178,184 ---- obj-$(CONFIG_MAPLE_KEYBOARD) += maple_keyb.o obj-$(CONFIG_MAPLE_MOUSE) += maplemouse.o + obj-$(CONFIG_MAPLE_LIGHTGUN) += maple_lg.o obj-$(CONFIG_BUSMOUSE) += busmouse.o obj-$(CONFIG_DTLK) += dtlk.o diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/joystick/maplecontrol.c ./drivers/char/joystick/maplecontrol.c *** ../linux-sh-orig/drivers/char/joystick/maplecontrol.c Mon Oct 15 13:44:59 2001 --- ./drivers/char/joystick/maplecontrol.c Tue Jan 15 05:17:29 2002 *************** *** 21,31 **** }; ! static void dc_pad_callback(struct mapleq *mq) { unsigned short buttons; ! struct maple_device *mapledev = mq->dev; ! struct dc_pad *pad = mapledev->private_data; struct input_dev *dev = &pad->dev; unsigned char *res = mq->recvbuf; --- 21,31 ---- }; ! static void dc_pad_callback(struct maple_driver_data *data) { unsigned short buttons; ! struct mapleq *mq=&data->mq; ! struct dc_pad *pad = data->private_data; struct input_dev *dev = &pad->dev; unsigned char *res = mq->recvbuf; *************** *** 73,82 **** } ! static int dc_pad_connect(struct maple_device *dev) { int i; ! unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); struct dc_pad *pad; const short btn_bit[32] = { --- 73,82 ---- } ! static int dc_pad_connect(struct maple_driver_data *d) { int i; ! unsigned long data = d->function_data; struct dc_pad *pad; const short btn_bit[32] = { *************** *** 97,103 **** return -1; memset(pad, 0, sizeof(struct dc_pad)); ! dev->private_data = pad; for (i=0; i<32; i++) if (data&(1<<i) && btn_bit[i]>=0) --- 97,103 ---- return -1; memset(pad, 0, sizeof(struct dc_pad)); ! d->private_data = pad; for (i=0; i<32; i++) if (data&(1<<i) && btn_bit[i]>=0) *************** *** 132,144 **** pad->dev.close = dc_pad_close; pad->dev.event = NULL; ! pad->dev.name = dev->product_name; pad->dev.idbus = BUS_MAPLE; input_register_device(&pad->dev); - maple_getcond_callback(dev, dc_pad_callback, 1, MAPLE_FUNC_CONTROLLER); - printk(KERN_INFO "input%d: controller(0x%lx): %s\n", pad->dev.number, data, pad->dev.name); --- 132,142 ---- pad->dev.close = dc_pad_close; pad->dev.event = NULL; ! pad->dev.name = d->dev->product_name; pad->dev.idbus = BUS_MAPLE; input_register_device(&pad->dev); printk(KERN_INFO "input%d: controller(0x%lx): %s\n", pad->dev.number, data, pad->dev.name); *************** *** 148,156 **** } ! static void dc_pad_disconnect(struct maple_device *dev) { ! struct dc_pad *pad = dev->private_data; input_unregister_device(&pad->dev); --- 146,154 ---- } ! static void dc_pad_disconnect(struct maple_driver_data *d) { ! struct dc_pad *pad = d->private_data; input_unregister_device(&pad->dev); *************** *** 165,170 **** --- 163,170 ---- name: "Dreamcast controller", connect: dc_pad_connect, disconnect: dc_pad_disconnect, + reply: dc_pad_callback, + vblank: maple_getcond_vblank_callback, }; diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maple_keyb.c ./drivers/char/maple_keyb.c *** ../linux-sh-orig/drivers/char/maple_keyb.c Thu Dec 6 07:23:03 2001 --- ./drivers/char/maple_keyb.c Wed Jan 16 00:44:16 2002 *************** *** 80,95 **** } ! static void dc_kbd_callback(struct mapleq *mq) { ! struct maple_device *mapledev = mq->dev; ! struct dc_kbd *kbd = mapledev->private_data; ! unsigned long *buf = mq->recvbuf; ! ! if (buf[1] == mapledev->function) { ! memcpy(kbd->new, buf+2, 8); ! dc_scan_kbd(kbd); ! } } --- 80,93 ---- } ! static void dc_kbd_callback(struct maple_driver_data *data) { ! struct mapleq *mq=& data->mq; ! struct dc_kbd *kbd = data->private_data; ! unsigned long *buf = (unsigned long *)mq->recvbuf; ! ! memcpy(kbd->new, buf+2, 8); ! dc_scan_kbd(kbd); } *************** *** 108,124 **** } ! static int dc_kbd_connect(struct maple_device *dev) { int i; ! unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); struct dc_kbd *kbd; if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL))) return -1; memset(kbd, 0, sizeof(struct dc_kbd)); ! dev->private_data = kbd; kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); --- 106,122 ---- } ! static int dc_kbd_connect(struct maple_driver_data *d) { int i; ! unsigned long data = d->function_data; struct dc_kbd *kbd; if (!(kbd = kmalloc(sizeof(struct dc_kbd), GFP_KERNEL))) return -1; memset(kbd, 0, sizeof(struct dc_kbd)); ! d->private_data = kbd; kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); *************** *** 131,143 **** kbd->dev.close = dc_kbd_close; kbd->dev.event = NULL; ! kbd->dev.name = dev->product_name; kbd->dev.idbus = BUS_MAPLE; input_register_device(&kbd->dev); - maple_getcond_callback(dev, dc_kbd_callback, 1, MAPLE_FUNC_KEYBOARD); - printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n", kbd->dev.number, data, kbd->dev.name); --- 129,139 ---- kbd->dev.close = dc_kbd_close; kbd->dev.event = NULL; ! kbd->dev.name = d->dev->product_name; kbd->dev.idbus = BUS_MAPLE; input_register_device(&kbd->dev); printk(KERN_INFO "input%d: keyboard(0x%lx): %s\n", kbd->dev.number, data, kbd->dev.name); *************** *** 147,155 **** } ! static void dc_kbd_disconnect(struct maple_device *dev) { ! struct dc_kbd *kbd = dev->private_data; input_unregister_device(&kbd->dev); --- 143,151 ---- } ! static void dc_kbd_disconnect(struct maple_driver_data *d) { ! struct dc_kbd *kbd = d->private_data; input_unregister_device(&kbd->dev); *************** *** 164,169 **** --- 160,167 ---- name: "Dreamcast keyboard", connect: dc_kbd_connect, disconnect: dc_kbd_disconnect, + reply: dc_kbd_callback, + vblank: maple_getcond_vblank_callback, }; diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maple_lg.c ./drivers/char/maple_lg.c *** ../linux-sh-orig/drivers/char/maple_lg.c Wed Dec 31 16:00:00 1969 --- ./drivers/char/maple_lg.c Tue Jan 22 11:39:07 2002 *************** *** 0 **** --- 1,271 ---- + /* + * $Id: maple_lg.c,v 1.1.1.1 2001/10/15 20:44:57 hubbe Exp $ + * SEGA Dreamcast light gun driver + * Based on drivers/maple/maplemouse.c + * + * Written by Fredrik Hubinette <hu...@hu...>, 2002 + * + * You may want to download xguncalibrate from + * http://fredrik.hubbe.net/xguncalibrate.tar.gz to + * calibrate your Lightgun. + * + */ + + #include <linux/kernel.h> + #include <linux/malloc.h> + #include <linux/input.h> + #include <linux/module.h> + #include <linux/init.h> + #include <linux/timer.h> + #include <linux/maple.h> + #include <linux/interrupt.h> + + #include <asm/dc_sysasic.h> + + MODULE_AUTHOR("Fredrik Hubinette <hu...@hu...>"); + MODULE_DESCRIPTION("SEGA Dreamcast light gun driver"); + + + #ifdef CONFIG_MAPLE_LG_DEBUG + # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) + #else + # define DPRINTK(fmt, args...) + #endif + + /* outx = inx * mult - sub */ + struct dc_lg_axis_param { + int mult; + int sub; + int max; + }; + + struct dc_lightgun { + struct maple_driver_data *data; + struct input_dev dev; + int open; + struct mapleq gun; + struct dc_lg_axis_param xparam, yparam; + }; + + + static void dc_lightgun_callback(struct maple_driver_data *data) + { + unsigned short buttons; + struct mapleq *mq=&data->mq; + struct dc_lightgun *lg = data->private_data; + struct input_dev *dev = &lg->dev; + unsigned char *res = mq->recvbuf; + + buttons = ~*(unsigned short *)(res+8); + + input_report_key(dev, BTN_LEFT, buttons&4); + input_report_key(dev, BTN_MIDDLE, buttons&8); + input_report_key(dev, BTN_RIGHT, buttons&2); + + input_report_key(dev, KEY_UP, buttons&0x0010); + input_report_key(dev, KEY_DOWN, buttons&0x0020); + input_report_key(dev, KEY_LEFT, buttons&0x0040); + input_report_key(dev, KEY_RIGHT,buttons&0x0080); + + } + + + static void dc_lightgun_pos_callback(void *privdata, int x, int y) + { + struct dc_lightgun *lg = (struct dc_lightgun *) privdata; + /* printk("\033\r Lightgun: %04x %04x \n",x,y); + printk(" Lightgun: %04x %04x \n",x,y); */ + + x=x * lg->xparam.mult - lg->xparam.sub; + if(x<0) x=0; + if(x >= lg->xparam.max) x = lg->xparam.max -1; + input_report_abs(& lg->dev, ABS_X, x); + + y=y * lg->yparam.mult - lg->yparam.sub; + if(y<0) y=0; + if(y >= lg->yparam.max) y = lg->yparam.max -1; + input_report_abs(& lg->dev, ABS_Y, y); + } + + + static int dc_lightgun_open(struct input_dev *dev) + { + struct dc_lightgun *lg = dev->private; + if( lg->open++ == 0) { + /* Enable lightgun functionality */ + + maple_set_gunmode(lg->data->dev->port, + dc_lightgun_pos_callback, lg); + } + return 0; + } + + static void dc_lightgun_close(struct input_dev *dev) + { + struct dc_lightgun *lg = dev->private; + if( --lg->open == 0) { + maple_set_gunmode(lg->data->dev->port, 0, 0); + } + } + + + void dc_lightgun_vblank_callback(struct maple_driver_data *data) + { + if( data->mq.done ) + { + data->mq.command = MAPLE_COMMAND_GETCOND; + data->mq.length = 1; + ((unsigned long *)data->mq.recvbuf)[0] = MAPLE_FUNC_CONTROLLER; + data->mq.sendbuf = data->mq.recvbuf; + + DPRINTK("queueing GETCOND for %d,%d,%x (%s)\n", + data->mq.port, + data->mq.unit, + data->driver->function, + data->driver->name); + + maple_add_packet(& data->mq ); + + } + } + + static int dc_lightgun_event(struct input_dev *dev, + unsigned int type, + unsigned int code, + int value) + { + struct dc_lightgun *lg = dev->private; + struct dc_lg_axis_param *tmp; + + #if 0 + printk(KERN_DEBUG " LGEV: %d %d %d\n",type,code,value); + #endif + + if(type != EV_FF) return -1; + switch(code & 0xf0) + { + default: return -2; + case 0: tmp = & lg->xparam; break; + case 1: tmp = & lg->yparam; break; + } + + switch(code & 0xf) + { + default: return -3; + case 0: tmp->mult=value; break; + case 1: tmp->sub=value; break; + case 2: tmp->max=value; break; + } + return 0; + } + + + static int dc_lightgun_connect(struct maple_driver_data *d) + { + unsigned long data = d->function_data; + struct dc_lightgun *lg; + + if (!(lg = kmalloc(sizeof(struct dc_lightgun), GFP_KERNEL))) + return -1; + memset(lg, 0, sizeof(struct dc_lightgun)); + + lg->data=d; + + lg->xparam.mult=1; + lg->xparam.sub =0xc8; + lg->xparam.max =640; + + lg->yparam.mult=1; + lg->yparam.sub =0x35; + lg->yparam.max =480; + + + d->private_data = lg; + + lg->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF); + lg->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + lg->dev.keybit[LONG(KEY_UP)] |= BIT(KEY_UP); + lg->dev.keybit[LONG(KEY_DOWN)] |= BIT(KEY_DOWN); + lg->dev.keybit[LONG(KEY_LEFT)] |= BIT(KEY_LEFT); + lg->dev.keybit[LONG(KEY_RIGHT)] |= BIT(KEY_RIGHT); + + lg->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y); + + /* FIXME: what should the max values really be? */ + lg->dev.absmax[ABS_X] = 640; + lg->dev.absmin[ABS_X] = 0; + lg->dev.absfuzz[ABS_X]= 20; + lg->dev.absflat[ABS_X]= 0; + + lg->dev.absmax[ABS_Y] = 480; + lg->dev.absmin[ABS_Y] = 0; + lg->dev.absfuzz[ABS_Y]= 20; + lg->dev.absflat[ABS_Y]= 0; + + lg->dev.private = lg; + lg->dev.open = dc_lightgun_open; + lg->dev.close = dc_lightgun_close; + lg->dev.event = dc_lightgun_event; + + lg->dev.name = d->dev->product_name; + lg->dev.idbus = BUS_MAPLE; + + + input_register_device(&lg->dev); + + printk(KERN_INFO "input%d: lightgun(0x%lx): %s\n", + lg->dev.number, data, lg->dev.name); + + MOD_INC_USE_COUNT; + + return 0; + } + + + static void dc_lightgun_disconnect(struct maple_driver_data *d) + { + struct dc_lightgun *lg = d->private_data; + + while( maple_del_packet( & lg->gun) < 0) + /* yield */; + + input_unregister_device(&lg->dev); + + kfree(lg); + + MOD_DEC_USE_COUNT; + } + + + static struct maple_driver dc_lightgun_driver = { + function: MAPLE_FUNC_LIGHTGUN | MAPLE_FUNC_CONTROLLER, + name: "Dreamcast light gun", + connect: dc_lightgun_connect, + disconnect: dc_lightgun_disconnect, + reply: dc_lightgun_callback, + vblank: dc_lightgun_vblank_callback, + }; + + + static int __init dc_lightgun_init(void) + { + + maple_register_driver(&dc_lightgun_driver); + return 0; + } + + + static void __exit dc_lightgun_exit(void) + { + maple_unregister_driver(&dc_lightgun_driver); + } + + + module_init(dc_lightgun_init); + module_exit(dc_lightgun_exit); + + /* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/char/maplemouse.c ./drivers/char/maplemouse.c *** ../linux-sh-orig/drivers/char/maplemouse.c Mon Oct 15 13:44:57 2001 --- ./drivers/char/maplemouse.c Tue Jan 15 05:18:22 2002 *************** *** 21,31 **** }; ! static void dc_mouse_callback(struct mapleq *mq) { int buttons, relx, rely, relz; ! struct maple_device *mapledev = mq->dev; ! struct dc_mouse *mouse = mapledev->private_data; struct input_dev *dev = &mouse->dev; unsigned char *res = mq->recvbuf; --- 21,31 ---- }; ! static void dc_mouse_callback(struct maple_driver_data *data) { int buttons, relx, rely, relz; ! struct mapleq *mq=& data->mq; ! struct dc_mouse *mouse = data->private_data; struct input_dev *dev = &mouse->dev; unsigned char *res = mq->recvbuf; *************** *** 58,73 **** } ! static int dc_mouse_connect(struct maple_device *dev) { ! unsigned long data = be32_to_cpu(dev->devinfo.function_data[0]); struct dc_mouse *mouse; if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) return -1; memset(mouse, 0, sizeof(struct dc_mouse)); ! dev->private_data = mouse; mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); --- 58,73 ---- } ! static int dc_mouse_connect(struct maple_driver_data *d) { ! unsigned long data = d->function_data; struct dc_mouse *mouse; if (!(mouse = kmalloc(sizeof(struct dc_mouse), GFP_KERNEL))) return -1; memset(mouse, 0, sizeof(struct dc_mouse)); ! d->private_data = mouse; mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); *************** *** 78,90 **** mouse->dev.close = dc_mouse_close; mouse->dev.event = NULL; ! mouse->dev.name = dev->product_name; mouse->dev.idbus = BUS_MAPLE; input_register_device(&mouse->dev); - maple_getcond_callback(dev, dc_mouse_callback, 1, MAPLE_FUNC_MOUSE); - printk(KERN_INFO "input%d: mouse(0x%lx): %s\n", mouse->dev.number, data, mouse->dev.name); --- 78,88 ---- mouse->dev.close = dc_mouse_close; mouse->dev.event = NULL; ! mouse->dev.name = d->dev->product_name; mouse->dev.idbus = BUS_MAPLE; input_register_device(&mouse->dev); printk(KERN_INFO "input%d: mouse(0x%lx): %s\n", mouse->dev.number, data, mouse->dev.name); *************** *** 94,102 **** } ! static void dc_mouse_disconnect(struct maple_device *dev) { ! struct dc_mouse *mouse = dev->private_data; input_unregister_device(&mouse->dev); --- 92,100 ---- } ! static void dc_mouse_disconnect(struct maple_driver_data *d) { ! struct dc_mouse *mouse = d->private_data; input_unregister_device(&mouse->dev); *************** *** 111,116 **** --- 109,116 ---- name: "Dreamcast mouse", connect: dc_mouse_connect, disconnect: dc_mouse_disconnect, + reply: dc_mouse_callback, + vblank: maple_getcond_vblank_callback, }; diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/maple/maple.c ./drivers/maple/maple.c *** ../linux-sh-orig/drivers/maple/maple.c Mon Oct 15 13:44:59 2001 --- ./drivers/maple/maple.c Tue Jan 22 11:20:04 2002 *************** *** 17,26 **** --- 17,59 ---- #include <asm/page.h> #include <asm/io.h> + #include <asm/dc_sysasic.h> #include <linux/maple.h> + /* + * Significantly rewritten by Fredrik Hubinette <hu...@hu...> 2002 + */ + + #if 0 #define DPRINTK(format, args...) printk(KERN_DEBUG format,##args) + #else + #define DPRINTK(format, args...) + #endif + + #if 0 + #define POLL_DPRINTK(format, args...) printk(KERN_INFO format,##args) + #else + #define POLL_DPRINTK(format, args...) + #endif + + #if 0 + #define IRQ_DPRINTK(format, args...) printk(KERN_INFO format,##args) + #else + #define IRQ_DPRINTK(format, args...) + #endif + + #if 0 + #define SETUP_DPRINTK(format, args...) printk(KERN_DEBUG format,##args) + #else + #define SETUP_DPRINTK(format, args...) + #endif + + #if 0 + #define DCOLOR(X) ((*(unsigned long *)0xa05f8040)=(X)) + #else + #define DCOLOR(X) + #endif #define MAPLE_PORTS 4 #define MAPLE_SCANHZ (HZ/100) *************** *** 31,106 **** #define MAPLE_DMA_SIZE (1<<MAPLE_DMA_ORDER) #define MAPLE_DMA_PAGES ((MAPLE_DMA_ORDER > PAGE_SHIFT) ? MAPLE_DMA_ORDER-PAGE_SHIFT : 0) ! static LIST_HEAD(maple_dev_list); ! static LIST_HEAD(maple_driver_list); ! static LIST_HEAD(maple_waitq); ! static LIST_HEAD(maple_sentq); ! static DECLARE_WAIT_QUEUE_HEAD(kmapled_wait); ! static DECLARE_COMPLETION(kmapled_exited); ! static int kmapled_pid = 0; ! struct timer_list maple_timer; ! unsigned long *maple_sendbuf, *maple_sendptr, *maple_lastptr; ! unsigned long maple_pnp_time; ! static struct maple_driver maple_dummy_driver = { ! function: 0, ! name: "Dummy", ! }; ! ! static void maple_dump_devinfo(struct maple_devinfo *devinfo) { ! DPRINTK(" function: 0x%08x\n", be32_to_cpu(devinfo->function)); ! DPRINTK(" data[0]: 0x%08x\n", be32_to_cpu(devinfo->function_data[0])); ! DPRINTK(" data[1]: 0x%08x\n", be32_to_cpu(devinfo->function_data[1])); ! DPRINTK(" data[2]: 0x%08x\n", be32_to_cpu(devinfo->function_data[2])); ! DPRINTK(" area code: %d\n", devinfo->area_code); ! DPRINTK(" direction: %d\n", devinfo->connector_direction); ! DPRINTK(" name: \"%-30.30s\"\n", devinfo->product_name); ! DPRINTK(" license: \"%-60.60s\"\n", devinfo->product_license); ! DPRINTK(" stanby power: %d\n", le16_to_cpu(devinfo->standby_power)); ! DPRINTK(" max power: %d\n", le16_to_cpu(devinfo->max_power)); } ! void maple_register_driver(struct maple_driver *driver) ! { ! struct list_head *lh = (void *)driver; ! list_add(lh, &maple_driver_list); ! ! MOD_INC_USE_COUNT; ! printk(KERN_INFO "maple: registered driver: %s (function 0x%lx)\n", ! driver->name, driver->function); ! } ! void maple_unregister_driver(struct maple_driver *driver) { ! struct list_head *lh = (void *)driver; ! list_del(lh); ! ! MOD_DEC_USE_COUNT; ! printk(KERN_INFO "maple: unregistered driver: %s (function 0x%lx)\n", ! driver->name, driver->function); } - static struct mapleq *maple_allocq(struct maple_device *dev) { - unsigned long buf; struct mapleq *mq; mq = kmalloc(sizeof(*mq), GFP_KERNEL); if (!mq) return NULL; ! mq->dev = dev; ! buf = (unsigned long)mq->buf; ! buf = (buf + 31) & ~31; ! mq->recvbuf = (void *)P2SEGADDR(buf); return mq; } --- 64,137 ---- #define MAPLE_DMA_SIZE (1<<MAPLE_DMA_ORDER) #define MAPLE_DMA_PAGES ((MAPLE_DMA_ORDER > PAGE_SHIFT) ? MAPLE_DMA_ORDER-PAGE_SHIFT : 0) ! /* Maple units: ! * 0 = primary ! * 1-5 = secondaries ! */ ! static int unitcodes[MAPLE_MAX_UNITS] = { 32, 1, 2, 4, 8, 16 }; ! ! static struct maple_port ports[MAPLE_PORTS]; ! static int maple_active_guns; ! static int maple_gunport; ! static int maple_active_gunport; ! /*** Heleper functions ***/ ! static void maple_detect_callback(struct mapleq *); ! static int maple_dma_done(void) { ! return (ctrl_inl(MAPLE_STATE) & 1) == 0; } ! #define MCP(P,U) do{ \ ! if( !(ports[(P)].dev[(U)]) != \ ! !(ports[(P)].known_units & unitcodes[(U)]) ) { \ ! unsigned long flags; \ ! printk(KERN_INFO "MF: %d (%d,%d) u=%x\n",__LINE__,(P),(U),ports[(P)].known_units); \ ! save_and_cli(flags); \ ! while(1); \ ! } \ ! }while(0) ! ! #define CHECKALLP() do{ \ ! int P,U; \ ! for(P=0;P<4;P++) for(U=0;U<MAPLE_MAX_UNITS;U++) MCP(P,U); \ ! }while(0) ! ! ! /*** Low-level maple functions *** ! * ! * This section only handles data transfers and callbacks, ! * it does not handle any devices, auto-detection or ! * timing. See the next section for that. ! */ ! unsigned long *maple_sendbuf, *maple_sendptr; + static LIST_HEAD(maple_waitq); + static LIST_HEAD(maple_sentq); ! void maple_init_mq(struct mapleq *mq) { ! unsigned long buf; ! mq->done = 1; ! buf = (unsigned long)mq->buf; ! buf = (buf + 31) & ~31; ! mq->recvbuf = (void *)P2SEGADDR(buf); } static struct mapleq *maple_allocq(struct maple_device *dev) { struct mapleq *mq; mq = kmalloc(sizeof(*mq), GFP_KERNEL); if (!mq) return NULL; ! maple_init_mq(mq); return mq; } *************** *** 114,206 **** kfree(mq); } ! static struct maple_device *maple_alloc_dev(int port, int unit) { ! struct maple_device *dev; ! dev = kmalloc(sizeof(*dev), GFP_KERNEL); ! if (!dev) ! return NULL; ! memset(dev, 0, sizeof(*dev)); ! dev->port = port; ! dev->unit = unit; ! dev->mq = maple_allocq(dev); ! if (!dev->mq) { ! kfree(dev); ! return NULL; } ! return dev; } ! static void maple_free_dev(struct maple_device *dev) { ! if (!dev) return; ! if (dev->mq) ! maple_freeq(dev->mq); ! kfree(dev); ! } ! static void maple_register_dev(struct maple_device *dev) ! { ! struct list_head *lh = (void *)dev; ! list_add_tail(lh, &maple_dev_list); ! } ! static void maple_unregister_dev(struct maple_device *dev) ! { ! struct list_head *lh = (void *)dev; ! list_del(lh); ! } - void maple_getcond_callback(struct maple_device *dev, - void (*callback)(struct mapleq *mq), - unsigned long interval, unsigned long function) - { - dev->callback = callback; - dev->interval = interval; - dev->function = cpu_to_be32(function); - dev->when = 0; } int maple_add_packet(struct mapleq *mq) { unsigned long flags; save_and_cli(flags); ! list_add((struct list_head *)mq, &maple_waitq); restore_flags(flags); ! return 0; } - int maple_del_packet(struct mapleq *mq) { struct list_head *lh; unsigned long flags; save_and_cli(flags); ! list_for_each(lh, &maple_sentq) { if (mq == (struct mapleq *)lh) { restore_flags(flags); ! return -1; } } ! list_for_each(lh, &maple_waitq) { if (mq == (struct mapleq *)lh) list_del(lh); --- 145,379 ---- kfree(mq); } + static unsigned long last_gun_data; ! static void maple_dma_irq(int irq, void *dev, struct pt_regs *fp) { ! struct mapleq *mq; ! struct list_head *lh, tmph; ! DCOLOR(0x0000ff); ! IRQ_DPRINTK("MAPLE DMA IRQ BEGIN\n"); ! POLL_DPRINTK("Maple DMA finished %d, %d.\n", ! list_empty(&maple_sentq), !maple_dma_done()); ! if (list_empty(&maple_sentq) || !maple_dma_done()) return; ! #if 1 ! if(maple_active_gunport >= 0) { ! int x,y; ! unsigned long tmp=*(unsigned long *)0xA05F80C4; ! if(last_gun_data != tmp) { ! y=tmp>>16; ! x=tmp&0xffff; ! POLL_DPRINTK("Maple gunport reply for port %d: " ! "%04x %04x\n", ! maple_active_gunport, ! x,y); ! ports[maple_active_gunport].gunmode( ! ports[maple_active_gunport].gunmode_data,x,y); ! maple_active_gunport=-1; ! } } + #endif ! list_for_each(lh, &maple_sentq) { ! tmph = *lh; ! ! mq = (struct mapleq *)lh; ! mq->done=1; ! POLL_DPRINTK("Maple reply (%d,%d) cmd=%d => %d\n", ! mq->port, mq->unit, ! mq->command, mq->recvbuf[0]); ! ! if(mq->recvbuf[0] == MAPLE_RESPONSE_NONE) ! maple_detect_callback(mq); ! else if(mq->callback) ! mq->callback(mq); ! ! lh = &tmph; ! ! } ! ! INIT_LIST_HEAD(&maple_sentq); ! maple_send(); ! ! IRQ_DPRINTK("MAPLE DMA IRQ END\n"); } ! ! ! /* Call this after one or more calls to maple_add_packet */ ! /* FIXME: implement scheduling */ ! static void low_maple_send(int vblank) { ! int i; ! int maple_packets; ! struct mapleq *mq; ! struct list_head *lh, tmph; ! unsigned long *maple_lastptr; ! unsigned long flags; ! ! /* Major-league dodgy */ ! save_and_cli(flags); ! ! if (!list_empty(&maple_sentq) || ! (maple_active_gunport != -1 && !vblank) ) { ! restore_flags(flags); return; + } ! if (list_empty(&maple_waitq) || !maple_dma_done()) { ! restore_flags(flags); ! return; ! } ! maple_packets = 0; ! maple_sendptr = maple_lastptr = maple_sendbuf; ! maple_active_gunport=-1; + list_for_each(lh, &maple_waitq) { + int port, unit, from, to, len; + unsigned long *lsendbuf; ! mq = (struct mapleq *)lh; ! ! port = mq->port; ! unit = mq->unit; ! len = mq->length & 0xff; ! ! if(ports[port].dev[unit] && ! ports[port].dev[unit]->lock) ! { ! if(vblank) ports[port].dev[unit]->lock--; ! continue; ! } + tmph = *lh; ! list_del(lh); ! list_add_tail(lh, &maple_sentq); + lsendbuf = mq->sendbuf; + + POLL_DPRINTK("Maple sending (%d,%d) cmd=%d len=%d\n", + mq->port, mq->unit, mq->command, len); + + from = port<<6; + to = (port<<6) | unitcodes[unit]; + + maple_lastptr = maple_sendptr; + + *maple_sendptr++ = (port<<16) | len; + *maple_sendptr++ = PHYSADDR(mq->recvbuf); + *maple_sendptr++ = mq->command | (to<<8) | + (from<<16) | (len<<24); + + #if 1 + while (len-->0) { + *maple_sendptr++ = *lsendbuf++; + } + #else + if(len > 0) + { + memcpy((char *)maple_sendptr, + (char *)lsendbuf, + sizeof(*maple_sendptr) * (len - 1)); + + maple_sendptr+=len; + } + #endif + if(mq->length & MAPLEQ_LENGTH_GUN) { + /* printk("Setting GUN MODE!\n"); */ + maple_lastptr = maple_sendptr; + *maple_sendptr++ = (port<<16) | 0x200; + maple_active_gunport = port; + break; + } + + lh = &tmph; + if (maple_packets++ > MAPLE_MAXPACKETS) + break; + } + + #if 0 + *maple_lastptr |= 0x80000000; /* Set 'last' bit */ + #else + switch(maple_active_guns) + { + case 0: + maple_active_gunport=-1; + *maple_lastptr |= 0x80000000; /* Set 'last' bit */ + break; + + default: + do { + if(++maple_gunport == MAPLE_PORTS) + maple_gunport=0; + } while( ! ports[maple_gunport].gunmode); + + /* FALL THROUGH */ + case 1: + maple_active_gunport=maple_gunport; + *maple_sendptr++ = (maple_gunport <<16) | 0x80000200; + POLL_DPRINTK("Active gunport = %d\n", + maple_active_gunport); + } + #endif + + if (maple_packets>0) { + for (i=0; i<(1<<MAPLE_DMA_PAGES); i++) + dma_cache_wback_inv(maple_sendbuf+i*PAGE_SIZE, PAGE_SIZE); + POLL_DPRINTK("Maple, starting DMA packets=%d\n",maple_packets); + ctrl_outl(1, MAPLE_STATE); + DCOLOR(0x00ffff); + } + + restore_flags(flags); } + void maple_send(void) + { + low_maple_send(0); + } int maple_add_packet(struct mapleq *mq) { + int ret=0; unsigned long flags; save_and_cli(flags); ! if(mq->done) { ! mq->done=0; ! list_add_tail((struct list_head *)mq, &maple_waitq); ! }else{ ! DPRINTK("Maple, rejecting package (to %d,%d, cmd=%d)\n", ! mq->port, mq->unit, ! mq->command); ! ret=-EINPROGRESS; ! } restore_flags(flags); ! return ret; } int maple_del_packet(struct mapleq *mq) { struct list_head *lh; unsigned long flags; save_and_cli(flags); ! list_for_each(lh, &maple_sentq) { if (mq == (struct mapleq *)lh) { restore_flags(flags); ! return -EBUSY; } } ! list_for_each(lh, &maple_waitq) { if (mq == (struct mapleq *)lh) list_del(lh); *************** *** 211,453 **** return 0; } ! ! static int maple_dma_done(void) { ! return (ctrl_inl(MAPLE_STATE) & 1) == 0; ! } ! static void maple_attach_driver(struct maple_device *dev) ! { ! struct list_head *lh; ! struct maple_driver *driver; ! unsigned long function; ! char *p, *recvbuf = dev->mq->recvbuf; - memcpy(&dev->devinfo, recvbuf+4, sizeof(dev->devinfo)); ! memcpy(dev->product_name, dev->devinfo.product_name, 30); ! memcpy(dev->product_license, dev->devinfo.product_license, 60); ! dev->product_name[30] = 0; ! dev->product_license[60] = 0; - for (p=dev->product_name+29; dev->product_name<=p; p--) - if (*p==' ') *p = 0; else break; - - for (p=dev->product_license+59; dev->product_license<=p; p--) - if (*p==' ') *p = 0; else break; ! function = be32_to_cpu(dev->devinfo.function); ! printk(KERN_INFO "maple(%d,%d): Connected(function 0x%lx)\n", ! dev->port, dev->unit, function); ! list_for_each(lh, &maple_driver_list) { ! driver = (struct maple_driver *)lh; ! if (function & driver->function) { ! if (!driver->connect(dev)) { ! dev->driver = driver; ! break; ! } ! } } ! if (dev->driver==NULL) { ! printk(KERN_INFO "maple(%d,%d): No proper driver.\n", ! dev->port, dev->unit); ! maple_dump_devinfo(&dev->devinfo); ! dev->driver = &maple_dummy_driver; } } ! ! static void maple_detach_driver(struct maple_device *dev) { ! printk(KERN_INFO "maple(%d,%d): Disconnected\n", ! dev->port, dev->unit); ! dev->driver->disconnect(dev); ! dev->driver = NULL; } ! static void maple_dma_irq(void) { ! struct mapleq *mq; ! struct maple_device *dev; ! struct list_head *lh, tmph; ! char *recvbuf; ! int code, need_wakeup = 0; - if (list_empty(&maple_sentq) || !maple_dma_done()) return; ! list_for_each(lh, &maple_sentq) { ! mq = (struct mapleq *)lh; ! dev = mq->dev; ! tmph = *lh; ! lh = &tmph; ! recvbuf = mq->recvbuf; ! code = recvbuf[0]; ! switch (code) { ! case MAPLE_RESPONSE_NONE: ! case MAPLE_RESPONSE_DEVINFO: ! maple_getcond_callback(dev, NULL, 0, 0); ! dev->event = code; ! need_wakeup = 1; ! break; - case MAPLE_RESPONSE_DATATRF: - if (dev->callback) - dev->callback(mq); - break; ! default: ! break; ! } ! } ! ! INIT_LIST_HEAD(&maple_sentq); ! if (need_wakeup) ! wake_up(&kmapled_wait); } ! static void maple_build_block(struct mapleq *mq) { ! int port, unit, from, to, len; ! unsigned long *lsendbuf = mq->sendbuf; ! port = mq->dev->port & 3; ! unit = mq->dev->unit; ! len = mq->length; ! from = port<<6; ! to = (port<<6) | (unit>0 ? (1<<(unit-1))&0x1f : 0x20); ! *maple_lastptr &= 0x7fffffff; ! maple_lastptr = maple_sendptr; ! *maple_sendptr++ = (port<<16) | len | 0x80000000; ! *maple_sendptr++ = PHYSADDR(mq->recvbuf); ! *maple_sendptr++ = mq->command | (to<<8) | (from<<16) | (len<<24); ! while (len-->0) { ! *maple_sendptr++ = *lsendbuf++; ! } ! } ! void maple_send(void) ! { ! int i; ! int maple_packets; ! struct mapleq *mq; ! struct list_head *lh, tmph; ! if (!list_empty(&maple_sentq)) ! return; - if (list_empty(&maple_waitq) || !maple_dma_done()) - return; ! maple_packets = 0; ! maple_sendptr = maple_lastptr = maple_sendbuf; ! list_for_each(lh, &maple_waitq) { ! mq = (struct mapleq *)lh; ! tmph = *lh; ! list_del(lh); ! list_add(lh, &maple_sentq); ! maple_build_block(mq); ! lh = &tmph; ! if (maple_packets++ > MAPLE_MAXPACKETS) break; } ! if (maple_packets>0) { ! for (i=0; i<(1<<MAPLE_DMA_PAGES); i++) ! dma_cache_wback_inv(maple_sendbuf+i*PAGE_SIZE, PAGE_SIZE); ! ctrl_outl(1, MAPLE_STATE); } ! } ! static void maple_timer_handler(unsigned long dummy) { struct list_head *lh; ! struct maple_device *dev; ! /* If DMA is done, dispatch callback functions */ ! maple_dma_irq(); ! /* Do task for each device */ ! list_for_each (lh, &maple_dev_list) { ! dev = (struct maple_device *)lh; ! if (dev->event) ! continue; ! if (dev->interval>0) { ! if (jiffies>dev->when) { ! dev->when = jiffies + dev->interval; ! dev->mq->command = MAPLE_COMMAND_GETCOND; ! dev->mq->sendbuf = &dev->function; ! dev->mq->length = 1; ! maple_add_packet(dev->mq); } } ! else { ! if (jiffies>=maple_pnp_time) { ! dev->mq->command = MAPLE_COMMAND_DEVINFO; ! dev->mq->length = 0; ! maple_add_packet(dev->mq); } } - } ! if (jiffies>=maple_pnp_time) ! maple_pnp_time = jiffies + MAPLE_PNP_INTERVAL; ! ! /* Scan list and build sending block */ ! maple_send(); ! /* Schedule next timer event */ ! mod_timer(&maple_timer, jiffies + MAPLE_SCANHZ); } ! static void maple_pnp_events(void) { ! struct maple_device *dev; ! struct list_head *lh; ! list_for_each(lh, &maple_dev_list) { ! dev = (struct maple_device *)lh; ! switch(dev->event) { ! case MAPLE_RESPONSE_NONE: ! if (dev->driver) ! maple_detach_driver(dev); ! break; ! case MAPLE_RESPONSE_DEVINFO: ! if (!dev->driver) ! maple_attach_driver(dev); ! break; ! } ! dev->event = 0; } } static int kmapled_thread(void *hoge) { lock_kernel(); --- 384,758 ---- return 0; } ! void maple_set_gunmode(int port, void (cb)(void *,int,int),void *data) { ! #if 1 ! unsigned long flags; + save_and_cli(flags); ! if( (!ports[port].gunmode) ^ (!cb) ) { ! maple_active_guns += (cb ? 1 : -1); ! if(cb) maple_gunport=port; ! } ! ports[port].gunmode = cb; ! ports[port].gunmode_data = data; ! restore_flags(flags); ! printk("Setting gunmode for port %d to %p(%p) (active guns = %d)\n",port,cb,data,maple_active_guns); ! #endif ! } ! static int init_maple_low(void) ! { ! int p, u; ! ! printk(KERN_INFO "SEGA Dreamcast MAPLE Bus drivers\n"); ! ! /* Allocate DMA buffer */ ! maple_sendbuf = (void *)__get_dma_pages(GFP_KERNEL, MAPLE_DMA_PAGES); ! if (maple_sendbuf == NULL) ! return -ENOMEM; ! memset(maple_sendbuf, 0, MAPLE_DMA_SIZE); ! ! for(p=0;p<MAPLE_PORTS;p++) ! { ! ports[p].port=p; ! ports[p].known_units=0; ! ports[p].units=0; ! ports[p].gunmode=0; ! ! for(u=0;u<MAPLE_MAX_UNITS;u++) ! ports[p].dev[u]=NULL; } + CHECKALLP(); ! /* Initialize hardware */ ! ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); ! ctrl_outl(0, MAPLE_RESET2); ! ctrl_outl(MAPLE_2MBPS|MAPLE_TIMEOUT(50000), MAPLE_SPEED); ! ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); ! ctrl_outl(1, MAPLE_ENABLE); ! ! ! if (request_irq(HW_EVENT_MAPLE_DMA, maple_dma_irq, 0, ! "Maple BUS DMA", 0)) { ! DPRINTK("couldn't register DMA int\n"); ! goto cleanup; } + return 0; + cleanup: + printk(KERN_INFO "maple: Register failed\n"); + return -ENOMEM; } ! static void exit_maple_low(void) { ! free_irq(HW_EVENT_MAPLE_DMA, 0); ! ! /* twiddle */ ! while(!maple_dma_done()); ! if (maple_sendbuf) ! free_pages((unsigned long)maple_sendbuf, MAPLE_DMA_PAGES); } ! ! /*** Maple Device interface *** ! * ! * This section provides the interface for individual ! * drivers and handles device state polling and auto-detection. ! */ ! ! static LIST_HEAD(maple_data_list); ! static LIST_HEAD(maple_driver_list); ! static DECLARE_WAIT_QUEUE_HEAD(kmapled_wait); ! static DECLARE_COMPLETION(kmapled_exited); ! static int kmapled_pid = 0; ! ! ! static int maple_detector_counter; ! static struct mapleq maple_detector; ! static int detector_new_dev; ! ! ! static void maple_dump_devinfo(struct maple_devinfo *devinfo) { ! DPRINTK(" function: 0x%08x\n", be32_to_cpu(devinfo->function)); ! DPRINTK(" data[0]: 0x%08x\n", be32_to_cpu(devinfo->function_data[0])); ! DPRINTK(" data[1]: 0x%08x\n", be32_to_cpu(devinfo->function_data[1])); ! DPRINTK(" data[2]: 0x%08x\n", be32_to_cpu(devinfo->function_data[2])); ! DPRINTK(" area code: %d\n", devinfo->area_code); ! DPRINTK(" direction: %d\n", devinfo->connector_direction); ! DPRINTK(" name: \"%-30.30s\"\n", devinfo->product_name); ! DPRINTK(" license: \"%-60.60s\"\n", devinfo->product_license); ! DPRINTK(" stanby power: %d\n", le16_to_cpu(devinfo->standby_power)); ! DPRINTK(" max power: %d\n", le16_to_cpu(devinfo->max_power)); ! } ! void maple_register_driver(struct maple_driver *driver) ! { ! struct list_head *lh = (void *)driver; ! list_add(lh, &maple_driver_list); ! MOD_INC_USE_COUNT; ! printk(KERN_INFO "maple: registered driver: %s (function 0x%lx)\n", ! driver->name, driver->function); ! } ! void maple_unregister_driver(struct maple_driver *driver) ! { ! struct list_head *lh = (void *)driver; ! list_del(lh); ! MOD_DEC_USE_COUNT; ! printk(KERN_INFO "maple: unregistered driver: %s (function 0x%lx)\n", ! driver->name, driver->function); } ! static struct maple_device *maple_alloc_dev(int port, int unit) { ! unsigned long flags; ! struct maple_device *dev; ! SETUP_DPRINTK("Maple, Allocating unit %d, %d\n",port, unit); ! dev = kmalloc(sizeof(*dev), GFP_KERNEL); ! if (!dev) ! return NULL; ! memset(dev, 0, sizeof(*dev)); ! dev->port = port; ! dev->unit = unit; ! dev->lock = 0; ! CHECKALLP(); + save_and_cli(flags); + ports[port].dev[unit]=dev; + ports[port].known_units |= unitcodes[unit]; + restore_flags(flags); ! CHECKALLP(); ! return dev; ! } ! void maple_getcond_vblank_callback(struct maple_driver_data *data) ! { ! if( data->mq.done ) ! { ! data->mq.command = MAPLE_COMMAND_GETCOND; ! data->mq.length = 1; ! ((unsigned long *)data->mq.recvbuf)[0] = ! cpu_to_be32(data->driver->function); ! data->mq.sendbuf = data->mq.recvbuf; ! ! POLL_DPRINTK("queueing GETCOND for %d,%d,%lx (%s)\n", ! data->mq.port, ! data->mq.unit, ! data->driver->function, ! data->driver->name); ! ! maple_add_packet(& data->mq ); ! } ! } ! static void maple_driver_cmd_callback(struct mapleq *mq) ! { ! struct maple_driver_data *data=(struct maple_driver_data *)mq->mq_privdata; ! if(data->driver->reply) ! data->driver->reply(data); ! } ! ! static int maple_connect(struct maple_device *dev, ! struct maple_driver *driver) ! { ! unsigned long flags, func; ! int tmp; ! struct maple_driver_data *data; ! data = kmalloc(sizeof(*data), GFP_KERNEL); ! ! if(!data) return -ENOMEM; ! data->driver=driver; ! data->dev=dev; ! data->private_data=0; ! ! /* Init mq as much as possible */ ! maple_init_mq(& data->mq ); ! data->mq.mq_privdata=data; ! data->mq.port=dev->port; ! data->mq.unit=dev->unit; ! data->mq.callback=maple_driver_cmd_callback; ! ! for(func=data->dev->function, tmp=0;;tmp++) { ! func &= func-1; ! if(data->driver->function > func) break; } + data->function_data=be32_to_cpu(dev->devinfo.function_data[tmp]); + SETUP_DPRINTK("Function data: %lx\n",data->function_data); ! if(! driver->connect(data)) { ! save_and_cli(flags); ! list_add((struct list_head *)data, &maple_data_list); ! restore_flags(flags); ! return 0; } ! kfree( data ); + CHECKALLP(); ! return 1; ! } ! ! static void maple_attach_driver(int port, int unit) { struct list_head *lh; ! struct maple_driver *driver; ! unsigned long function, bits_left; ! char *p, *recvbuf = maple_detector.recvbuf; ! struct maple_device *dev=maple_alloc_dev(port, unit); ! memcpy(&dev->devinfo, recvbuf+4, sizeof(dev->devinfo)); ! ! memcpy(dev->product_name, dev->devinfo.product_name, 30); ! memcpy(dev->product_license, dev->devinfo.product_license, 60); ! dev->product_name[30] = 0; ! dev->product_license[60] = 0; ! ! for (p=dev->product_name+29; dev->product_name<=p; p--) ! if (*p==' ') *p = 0; else break; ! ! for (p=dev->product_license+59; dev->product_license<=p; p--) ! if (*p==' ') *p = 0; else break; ! ! function = be32_to_cpu(dev->devinfo.function); ! dev->function=function; ! ! printk(KERN_INFO "maple(%d,%d): Connected(function 0x%lx)\n", ! dev->port, dev->unit, function); ! ! /* First, try all functions of this device */ ! for(bits_left=function;bits_left;) { ! int best_bits=0; ! ! /* Find out the maximum number of functions we can get ! * from one driver. ! */ ! list_for_each(lh, &maple_driver_list) { ! unsigned long bits, tmp; ! ! driver = (struct maple_driver *)lh; ! if(driver->function & ~bits_left) continue; ! tmp=bits_left & driver->function; ! bits=0; ! while(tmp) ! { ! tmp = tmp & (tmp -1); ! bits++; } + if(bits > best_bits) + best_bits=bits; } ! ! /* Now, first try the best drivers, ! * then the second best, etc. ! */ ! for(;best_bits>0;best_bits--) { ! list_for_each(lh, &maple_driver_list) { ! unsigned long bits, tmp; ! ! driver = (struct maple_driver *)lh; ! if(driver->function & ~bits_left) continue; ! tmp=bits_left & driver->function; ! bits=0; ! while(tmp) { ! tmp = tmp & (tmp -1); ! bits++; ! } ! if(bits == best_bits && ! !maple_connect(dev,driver)) { ! bits_left=bits_left & ! ~driver->function; ! goto check_more_bits; ! ! } } } ! printk(KERN_INFO "maple(%d,%d): No driver for function(s): %lx.\n", ! dev->port, dev->unit, bits_left); ! maple_dump_devinfo(&dev->devinfo); ! break; ! check_more_bits: ; ! } ! ! CHECKALLP(); } ! static void maple_detach_driver(int port, int unit) { ! unsigned long flags; ! struct list_head *lh, tmph; ! struct maple_device *dev=ports[port].dev[unit]; ! if(!dev) ! return; ! printk(KERN_INFO "maple(%d,%d): Disconnected (%d,%d)\n", ! dev->port, dev->unit, port, unit); ! list_for_each(lh, &maple_data_list) { ! struct maple_driver_data *data; ! data=(struct maple_driver_data *)lh; ! if(data->dev == dev) { ! save_and_cli(flags); ! tmph=*lh; ! list_del(lh); ! restore_flags(flags); ! ! data->driver->disconnect(data); ! while( maple_del_packet( & data->mq ) < 0) ! /* yield() */; + kfree(lh); + lh=&tmph; + } } + + save_and_cli(flags); + ports[port].dev[unit]=0; + ports[port].known_units &=~ unitcodes[unit]; + restore_flags(flags); + + CHECKALLP(); + + kfree(dev); } + + /* kmapled */ static int kmapled_thread(void *hoge) { lock_kernel(); *************** *** 457,463 **** strcpy(current->comm, "kmapled"); do { ! maple_pnp_events(); interruptible_sleep_on(&kmapled_wait); } while(!signal_pending(current)); --- 762,804 ---- strcpy(current->comm, "kmapled"); do { ! /*Yawn*/ ! /* Time to see if there are any new devices */ ! ! SETUP_DPRINTK("kmapled: *yawn*\n"); ! CHECKALLP(); ! ! int p, u; ! ! /* maple_detector contains devinfo for a new device ? */ ! if(detector_new_dev) { ! maple_attach_driver(maple_detector.port, ! maple_detector.unit); ! detector_new_dev=0; ! maple_detector.done=1; /* allow more autodetects */ ! } ! ! for(p=0;p<MAPLE_PORTS;p++) { ! if(!(ports[p].known_units & ~ports[p].units)) ! continue; ! ! SETUP_DPRINTK("Maple, port[%d] units=%x known_units=%x\n", ! p, ! ports[p].units, ! ports[p].known_units); ! ! for(u=0;u<MAPLE_MAX_UNITS;u++) { ! if(unitcodes[u] & ! ports[p].known_units & ! ~ports[p].units) { ! maple_detach_driver(p,u); ! } ! } ! } ! ! CHECKALLP(); ! ! /* Wait for another event */ interruptible_sleep_on(&kmapled_wait); } while(!signal_pending(current)); *************** *** 466,495 **** complete_and_exit(&kmapled_exited, 0); } ! static int __init maple_init(void) { ! struct maple_device *dev; ! int i; ! printk(KERN_INFO "SEGA Dreamcast MAPLE Bus drivers\n"); ! /* Allocate DMA buffer */ ! maple_sendbuf = (void *)__get_dma_pages(GFP_KERNEL, MAPLE_DMA_PAGES); ! if (maple_sendbuf == NULL) ! return -ENOMEM; ! memset(maple_sendbuf, 0, MAPLE_DMA_SIZE); ! /* Register dummy maple driver */ ! maple_register_driver(&maple_dummy_driver); ! /* Register basic port devices */ ! for (i=0; i<MAPLE_PORTS; i++) { ! dev = maple_alloc_dev(i, 0); ! if (!dev) ! goto cleanup; ! maple_register_dev(dev); } /* Start kernel thread */ kmapled_pid = kernel_thread(kmapled_thread, NULL, --- 807,958 ---- complete_and_exit(&kmapled_exited, 0); } + /* + * If there is an unconfigure device on this port, + * send a DEVINFO request to it. + */ + static int maple_detect_resend(void) + { + int u,p; + if(! maple_detector.done ) return 0; + + CHECKALLP(); + + p=maple_detector.port; + if(ports[p].units & ~ports[p].known_units) { + for(u=0;u<MAPLE_MAX_UNITS;u++) { + if(unitcodes[u] & + ports[p].units & + ~ports[p].known_units) { + SETUP_DPRINTK("Detector resender, sending devinfo to unit 0 on port %d\n",p); + maple_detector.command=MAPLE_COMMAND_DEVINFO; + maple_detector.port = p; + maple_detector.unit = u; + maple_detector.length = 0; + maple_add_packet(& maple_detector); + return 1; + } + } + } + return 0; + } ! ! /* ! * Autodetect DMA callback. ! * 1) Wake up kmapled when we receive a MAPLE_RESPONSE_DEVINFO ! * for an unconfigured device. ! * 2) Wake up kmapled if devices have been removed. ! */ ! static void maple_detect_callback(struct mapleq *mq) { ! int p=maple_detector.port; ! int u=maple_detector.unit; ! /* SETUP_DPRINTK("Detector callback.\n"); */ ! CHECKALLP(); ! switch(mq->recvbuf[0]) { ! case MAPLE_RESPONSE_DEVINFO: ! SETUP_DPRINTK("Detector reply DEVINFO (%d, %d), units = %x.\n",p,u,mq->recvbuf[2] & 0x3f); ! if(!u) ! ports[p].units = mq->recvbuf[2] & 0x3f; ! else ! ports[p].units |= unitcodes[u]; ! ! if(!ports[p].dev[u]) { ! maple_detector.done=0; ! detector_new_dev=1; ! SETUP_DPRINTK("Kmapled, wakie wakie (new device) ... "); ! wake_up(&kmapled_wait); ! return; ! } ! break; ! case MAPLE_RESPONSE_NONE: ! SETUP_DPRINTK("Detector reply NONE (%d,%d)\n",p,u); ! if(!u) ! ports[p].units = 0; ! else ! ports[p].units &=~ unitcodes[u]; ! break; ! } ! maple_detect_resend(); ! ! /* Devices are gone */ ! if(ports[p].known_units & ~ports[p].units) { ! SETUP_DPRINTK("Kmapled, wakie wakie (device gone) ... "); ! wake_up(&kmapled_wait); } + } + + /* + * Autodetect vertical blank callback + * Check if there are subdevices to configure. + * If not, check if it is time to send a COMMAND_DEVINFO + * to a primary device on a port. + */ + static void maple_detect_vblank(void) + { + int p; + + CHECKALLP(); + + if(! maple_detector.done ) return; + if( maple_detect_resend() ) return; + + maple_detector_counter++; + if(maple_detector_counter & 0xf) return; + p = (maple_detector_counter >> 4 ) & 3; + + SETUP_DPRINTK("Detector, sending devinfo to unit 0 on port %d\n",p); + + maple_detector.command=MAPLE_COMMAND_DEVINFO; + maple_detector.port = p; + maple_detector.unit = 0; + maple_detector.length = 0; + maple_detector.callback = maple_detect_callback; + maple_add_packet(& maple_detector); + } + + static void maple_vblank_interupt(int irq, void *dev, struct pt_regs *fp) + { + struct list_head *lh; + struct maple_driver_data *data; + + IRQ_DPRINTK("MAPLE VBLANK IRQ BEGIN\n"); + DCOLOR(0xff00ff); + + /* Dodgyness, damn maple! */ + if(maple_active_gunport >=0) maple_dma_irq(0,0,0); + + + maple_detect_vblank(); + + + /* Do task for each device */ + list_for_each (lh, &maple_data_list) { + data = (struct maple_driver_data *)lh; + if(data->driver->vblank) + data->driver->vblank(data); + } + + /* Scan list and build sending block */ + low_maple_send(1); + + IRQ_DPRINTK("MAPLE VBLANK IRQ END\n"); + } + + + static int __init maple_init(void) + { + int i; + + if((i=init_maple_low())) + return i; + + maple_init_mq(& maple_detector ); /* Start kernel thread */ kmapled_pid = kernel_thread(kmapled_thread, NULL, *************** *** 497,517 **** if (kmapled_pid==0) goto cleanup; ! /* Start to scan ports */ ! maple_pnp_time = 0; ! ! /* Initialize hardware */ ! ctrl_outl(MAPLE_MAGIC, MAPLE_RESET); ! ctrl_outl(0, MAPLE_RESET2); ! ctrl_outl(MAPLE_2MBPS|MAPLE_TIMEOUT(50000), MAPLE_SPEED); ! ctrl_outl(PHYSADDR(maple_sendbuf), MAPLE_DMAADDR); ! ctrl_outl(1, MAPLE_ENABLE); ! ! /* Initialize timer */ ! init_timer(&maple_timer); ! maple_timer.expires = jiffies + MAPLE_SCANHZ; ! maple_timer.function = maple_timer_handler; ! add_timer(&maple_timer); return 0; --- 960,970 ---- if (kmapled_pid==0) goto cleanup; ! if (request_irq(HW_EVENT_VSYNC, maple_vblank_interupt, SA_SHIRQ, ! "Maple BUS", 0)) { ! DPRINTK("couldn't register VBL int\n"); ! goto cleanup; ! } return 0; *************** *** 526,549 **** { /* XXX: Must do proper clean-up */ kill_proc(kmapled_pid, SIGTERM, 1); wait_for_completion(&kmapled_exited); ! if (maple_sendbuf) ! free_pages((unsigned long)maple_sendbuf, MAPLE_DMA_PAGES); ! ! del_timer(&maple_timer); } module_init(maple_init); module_exit(maple_exit); EXPORT_SYMBOL(maple_add_packet); EXPORT_SYMBOL(maple_del_packet); EXPORT_SYMBOL(maple_register_driver); EXPORT_SYMBOL(maple_unregister_driver); ! EXPORT_SYMBOL(maple_getcond_callback); /* * Local variables: --- 979,1007 ---- { /* XXX: Must do proper clean-up */ + free_irq(HW_EVENT_VSYNC, 0); + kill_proc(kmapled_pid, SIGTERM, 1); wait_for_completion(&kmapled_exited); ! exit_maple_low(); } module_init(maple_init); module_exit(maple_exit); + EXPORT_SYMBOL(maple_init_mq); + EXPORT_SYMBOL(maple_allocq); + EXPORT_SYMBOL(maple_freeq); EXPORT_SYMBOL(maple_add_packet); EXPORT_SYMBOL(maple_del_packet); + EXPORT_SYMBOL(maple_send); + EXPORT_SYMBOL(maple_set_gunmode); + EXPORT_SYMBOL(maple_register_driver); EXPORT_SYMBOL(maple_unregister_driver); ! EXPORT_SYMBOL(maple_getcond_vblank_callback); /* * Local variables: diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/drivers/video/pvr2fb.c ./drivers/video/pvr2fb.c *** ../linux-sh-orig/drivers/video/pvr2fb.c Mon Oct 15 13:45:05 2001 --- ./drivers/video/pvr2fb.c Tue Jan 8 01:17:00 2002 *************** *** 1051,1057 **** return -EINVAL; } ! if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, 0, "pvr2 VBL handler", ¤tpar)) { DPRINTK("couldn't register VBL int\n"); return -EBUSY; --- 1051,1057 ---- return -EINVAL; } ! if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, SA_SHIRQ, "pvr2 VBL handler", ¤tpar)) { DPRINTK("couldn't register VBL int\n"); return -EBUSY; diff -C3 -N -r --exclude=CVS --exclude *~ ../linux-sh-orig/include/linux/maple.h ./include/linux/maple.h *** ../linux-sh-orig/include/linux/maple.h Mon Oct 15 13:45:12 2001 --- ./include/linux/maple.h Tue Jan 22 11:36:22 2002 *************** *** 64,70 **** #define MAPLE_FUNC_PURUPURU 0x100 #define MAPLE_FUNC_MOUSE 0x200 - struct maple_devinfo { unsigned long function; /* big endian! */ unsigned long function_data[3]; /* big endian! */ --- 64,69 ---- *************** *** 80,127 **** struct maple_driver; struct maple_device; struct mapleq { struct list_head list; ! struct maple_device *dev; ! void *sendbuf, *recvbuf; ! unsigned char command, length; unsigned char buf[1024+32]; }; struct maple_device { ! struct list_head list; ! struct maple_driver *driver; ! struct mapleq *mq; ! void *private_data; ! void (*callback)(struct mapleq *mq); ! unsigned long when, interval, function; ! int event; struct maple_devinfo devinfo; ! unsigned char port, unit; char product_name[32]; char product_license[64]; }; struct maple_driver { struct list_head list; ! unsigned long function; const char *name; ! int (*connect)(struct maple_device *dev); ! void (*disconnect)(struct maple_device *dev); }; int maple_add_packet(struct mapleq *mq); int maple_del_packet(struct mapleq *mq); - void maple_register_driver(struct maple_driver *driver); void maple_unregister_driver(struct maple_driver *driver); - void maple_getcond_callback(struct maple_device *dev, - void (*callback)(struct mapleq *mq), - unsigned long interval, unsigned long function); /* * Local variables: --- 79,159 ---- struct maple_driver; struct maple_device; + + #define MAPLEQ_LENGTH_GUN (1<<9) + struct mapleq { struct list_head list; ! void *mq_privdata; ! void *sendbuf; ! char *recvbuf; /* points to ->buf, but 32-byte aligned */ ! ! unsigned char port, unit; ! unsigned char command; ! unsigned char done; ! int length; ! ! void (*callback)(struct mapleq *mq); unsigned char buf[1024+32]; }; struct maple_device { ! unsigned char port; ! unsigned char unit; ! short lock; ! ! unsigned long function; /* Little endian */ struct maple_devinfo devinfo; ! char product_name[32]; char product_license[64]; }; + /* Max devices per port */ + #define MAPLE_MAX_UNITS 6 + + struct maple_port { + unsigned char port; + unsigned char known_units; /* connected units */ + unsigned char units; /* units to connect/disconnect */ + void (*gunmode)(void *,int,int); + void *gunmode_data; + struct maple_device *dev[MAPLE_MAX_UNITS]; + }; + + struct maple_driver_data { + struct list_head list; + struct maple_driver *driver; + struct maple_device *dev; + void *private_data; + unsigned long function_data; + struct mapleq mq; + }; struct maple_driver { struct list_head list; ! unsigned long function; /* One or more bits */ const char *name; ! ! void (*vblank)(struct maple_driver_data *dev); ! void (*reply)(struct maple_driver_data *dev); ! int (*connect)(struct maple_driver_data *dev); ! void (*disconnect)(struct maple_driver_data *dev); }; + /* Prototypes begin here */ + void maple_send(void); int maple_add_packet(struct mapleq *mq); int maple_del_packet(struct mapleq *mq); void maple_register_driver(struct maple_driver *driver); void maple_unregister_driver(struct maple_driver *driver); + void maple_getcond_vblank_callback(struct maple_driver_data *data); + void maple_set_gunmode(int, void (*)(void *,int,int), void *); + void maple_init_mq(struct mapleq *mq); + /* Prototypes end here */ /* * Local variables: |
From: Adrian M. <ad...@mc...> - 2002-01-18 00:31:45
|
This type of query should be directed to your Sega Developer Account Representative. -- Kent Lottis (ken...@mi...) Microsoft Even though you may smirk, I must say: "This posting is provided "AS IS" with no warranties, and confers no rights. You assume all risk for your use. © 2001 Microsoft Corporation. All rights reserved." ------------------------------ "Adrian McMenamin" <ad...@mc...> wrote in message news:3c4...@ne...... > Can anyone here point me in the direction of information about how to > access the DSP (as opposed to sound more generally) on the Dreamcast? > -- > Adrian McMenamin > ad...@mc... From a WindowsCE newsgroup ... and the serf cannot even post properly. |
From: M. R. B. <mr...@0x...> - 2002-01-18 00:28:33
|
* Adrian McMenamin <ad...@mc...> on Fri, Jan 18, 2002: > Please find below replacement (and better) patch for the sound driver. (N= B:=20 > This is a patch against the code in the linuxdc cvs). A copy of the patch= ed=20 > file - but named aica.c is also available at=20 > http://www.mcmen.demon.co.uk/linuxdc/aica/aica.c =20 Curious - why aren't you committing these to CVS after posting them to the list? M. R. |