vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / generic / mpu401 / mpu401.c
blobcb61dc3f28fdce241391fe15b7ebb31663eec3c3
1 /*
2 * Copyright 2003-2006, Haiku.
3 * Distributed under the terms of the MIT License.
5 * A module driver for the generic mpu401 midi interface.
7 * Author:
8 * Greg Crain (gsc70@comcast.net)
10 * mpu401.c
14 #include <ISA.h>
15 #include <KernelExport.h>
16 #include <OS.h>
17 #include <midi_driver.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include "debug.h"
22 #include "mpu401_priv.h"
25 /* ----------
26 midi_create_device -
27 ----- */
28 /*-----------------------------*/
29 /* Version 1 of mpu401 module */
30 /*-----------------------------*/
32 static status_t
33 create_device(int port, void ** out_storage, uint32 workarounds,
34 void (*interrupt_op)(int32 op, void * card), void * card)
36 mpu401device mpu_device;
37 mpu401device *mpuptr;
39 /* fill the structure with specific info from caller */
40 mpu_device.addrport = port;
41 mpu_device.workarounds = workarounds;
42 mpu_device.V2 = FALSE;
43 mpu_device.count = 1;
44 mpu_device.interrupt_op = interrupt_op;
45 mpu_device.card = card;
47 LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
48 mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
50 // basically, each call to create device allocates memory for
51 //a structure with the specific device info. The pointer to this is
52 // returned back to calling driver
53 mpuptr = (mpu401device*)malloc (sizeof(mpu401device));
54 memcpy(mpuptr, &mpu_device, sizeof(mpu_device));
55 *out_storage = (void *)mpuptr;
57 return B_OK;
61 /*-----------------------------*/
62 /* Version 2 of mpu401 module */
63 /*-----------------------------*/
65 static status_t
66 create_device_v2(int port, void ** out_storage, uint32 workarounds,
67 void (*interrupt_op)(int32 op, void * card), void * card)
69 mpu401device *mpuptr;
70 mpu401device mpu_device;
72 // not sure exactly how v2 of the module works. I think that two ports
73 // are created. One for midi in data, and another for midi out data.
74 // Instead of transfering data using a buffer and pointer, the midi
75 // data is transfered via the global ports.
76 // If the ports are created in the midi server, then the port id's
77 // should be known in this hook call.
78 // If the ports are created in this hook call, the the port id's
79 // should be returned to the midi server.
81 // One version of read/write hook functions are used for both v1, v2.
82 // Therefore, in those calls, it needs to be known whether the mididata
83 // is to be read/written to a buffer, or to the port.
85 mpu_device.addrport = port;
86 mpu_device.workarounds = workarounds;
87 mpu_device.V2 = TRUE;
88 mpu_device.count = 1;
89 mpu_device.card = card;
91 LOG(("create_device count= %ld, addrport 0x%x, workarounds: %d",
92 mpu_device.count, mpu_device.addrport, mpu_device.workarounds));
94 mpuptr = (mpu401device*)malloc(sizeof(mpu401device));
95 memcpy(mpuptr, &mpu_device, sizeof(mpu_device));
96 *out_storage = (void *)mpuptr;
98 return B_OK;
102 /* ----------
103 midi_delete_device
104 ----- */
105 static status_t
106 delete_device(void * storage)
108 mpu401device * mpu_device = (mpu401device *)storage;
110 LOG(("device->addrport= 0x%x count= %ld\n",
111 mpu_device->addrport, mpu_device->count));
112 LOG(("delete_device: *storage:%p\n", storage));
114 free(mpu_device); // free the memory allocated in create_device
116 return B_OK;
120 /* ----------
121 midi_open - handle open() calls
122 ----- */
124 static status_t
125 midi_open(void * storage, uint32 flags, void ** out_cookie)
127 char semname[25];
128 int ack_byte;
129 mpu401device * mpu_device = (mpu401device *)storage;
131 LOG(("open() flags: %ld, *storage: %p, **out_cookie: %p\n", flags,
132 storage, out_cookie));
133 LOG(("open: device->addrport 0x%x ,workarounds 0x%x\n",
134 mpu_device->addrport, mpu_device->workarounds));
136 // the undocumented V2 module is not complete
137 // we will allow the device to be created since some drivers depend on it
138 // but will return an error if the actual midi device is opened:
139 if (mpu_device->V2 == TRUE)
140 return B_ERROR;
142 switch (mpu_device->workarounds) {
143 case 0x11020004: // Still required for Creative Audigy, Audigy2
144 case 0x11020005:
145 case 0:
146 // don't know the current mpu state
147 PRINT(("reset MPU401\n"));
148 Write_MPU401(mpu_device->addrport, UARTCMD,
149 mpu_device->workarounds, MPU401_RESET);
150 snooze(30000);
151 Write_MPU401(mpu_device->addrport, UARTCMD,
152 mpu_device->workarounds, MPU401_RESET);
153 snooze(30000);
154 ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
155 mpu_device->workarounds);
156 PRINT(("enable UART mode\n"));
157 Write_MPU401(mpu_device->addrport, UARTCMD,
158 mpu_device->workarounds, MPU401_UART);
159 snooze(30000);
160 ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
161 mpu_device->workarounds);
162 PRINT(("port cmd ack is 0x%x\n", ack_byte));
163 *out_cookie = mpu_device;
164 break;
165 case 0x14121712:
166 PRINT(("reset MPU401\n"));
167 Write_MPU401(mpu_device->addrport, UARTDATA,
168 mpu_device->workarounds, 0x00);
169 snooze(30000);
170 Write_MPU401(mpu_device->addrport, UARTCMD,
171 mpu_device->workarounds, MPU401_RESET);
172 snooze(30000);
173 ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
174 mpu_device->workarounds);
175 PRINT(("enable UART mode\n"));
176 Write_MPU401(mpu_device->addrport, UARTDATA,
177 mpu_device->workarounds, 0x00);
178 snooze(30000);
179 Write_MPU401(mpu_device->addrport, UARTCMD,
180 mpu_device->workarounds, MPU401_UART);
181 snooze(30000);
182 ack_byte = Read_MPU401(mpu_device->addrport, UARTDATA,
183 mpu_device->workarounds);
184 PRINT(("port cmd ack is 0x%x\n", ack_byte));
185 break;
186 case 1:
187 // Some devices are always in UART mode
188 PRINT(("already in UART mode\n"));
189 break;
190 default:
191 PRINT(("Unknown workaround: %d\n", mpu_device->workarounds));
192 break;
193 } //end switch
195 // Create Read semaphore for midi-in data
196 sprintf(semname, "mpu401:%04x:read_sem", mpu_device->addrport);
197 mpu_device->readsemaphore = create_sem(0, semname);
199 // Create Write semaphore for midi-out data
200 sprintf(semname,"mpu401:%04x:write_sem", mpu_device->addrport);
201 mpu_device->writesemaphore = create_sem(1, semname);
203 // clear midi-in buffer
204 mbuf_bytes = 0;
205 mbuf_current = 0;
206 mbuf_start = 0;
208 //Enable midi interrupts
209 mpu_device->interrupt_op(B_MPU_401_ENABLE_CARD_INT, mpu_device->card);
211 if ((mpu_device->readsemaphore > B_OK)
212 && (mpu_device->writesemaphore > B_OK)) {
213 atomic_add(&mpu_device->count, 1);
214 PRINT(("midi_open() done (count = %x)\n", open_count));
215 return B_OK;
217 return B_ERROR;
221 /* ----------
222 midi_close - handle close() calls
223 ----- */
224 static status_t
225 midi_close(void * cookie)
227 mpu401device * mpu_device = (mpu401device *)cookie;
229 if (mpu_device->count <= 0)
230 return B_ERROR;
232 //Disable soundcard midi interrupts
233 mpu_device->interrupt_op(B_MPU_401_DISABLE_CARD_INT, mpu_device->card);
235 // Delete the semaphores
236 delete_sem(mpu_device->readsemaphore);
237 delete_sem(mpu_device->writesemaphore);
239 atomic_add(&mpu_device->count, -1);
240 PRINT(("midi_close() done (count = %" B_PRId32 ")\n", mpu_device->count));
242 return B_OK;
246 /* ----------
247 midi_free - free up allocated memory
248 ----- */
249 static status_t
250 midi_free(void * cookie)
252 LOG(("midi_free()\n"));
253 return B_OK;
257 /* ----------
258 midi_control - handle control() calls
259 ----- */
260 static status_t
261 midi_control(void * cookie, uint32 op, void * data, size_t len)
263 //mpu401device *mpu_device = (mpu401device *)cookie;
265 /* I don't think this is ever called ...*/
266 LOG(("midi_control()\n"));
267 return B_OK;
271 /* ----------
272 midi_read - handle read() calls
273 ----- */
274 static status_t
275 midi_read(void *cookie, off_t pos, void *buffer, size_t *num_bytes)
277 /* The actual midi data is read from the device in the interrupt handler;
278 this reads and returns the data from a buffer */
280 unsigned char *data;
281 unsigned int i;
282 cpu_status status __attribute__((unused));
283 status_t bestat;
284 mpu401device *mpu_device = (mpu401device *)cookie;
286 data = (unsigned char*)buffer;
288 i = 0;
289 *num_bytes = 0;
290 bestat = acquire_sem_etc(mpu_device->readsemaphore, 1,
291 B_CAN_INTERRUPT, 0);
292 if (bestat == B_INTERRUPTED) {
293 //PRINT(("acquire_sem B_INTERRUPTED!\n"));
294 return B_INTERRUPTED;
296 if (bestat != B_OK) {
297 TRACE(("acquire_sem not B_OK %d\n",(int)bestat));
298 *num_bytes = 1;
299 return B_INTERRUPTED;
300 } else {
301 #ifdef __HAIKU__
302 if (user_memcpy(data+i, &(mpubuffer[mbuf_start]),
303 sizeof(unsigned char)) == B_OK) {
304 #else
305 status = lock();
306 *(data+i) = mpubuffer[mbuf_start];
307 #endif
308 i++;
309 mbuf_start++; // pointer to data in ringbuffer
310 if (mbuf_start >= (MBUF_ELEMENTS-1))
311 mbuf_start = 0; //wraparound of ringbuffer
312 *num_bytes = 1; // How many bytes are being returned in buffer
313 if (mbuf_bytes > 0)
314 mbuf_bytes--; // bytes read from buffer, so decrement buffer count
315 #ifdef __HAIKU__
317 #else
318 unlock(status);
319 #endif
320 //PRINT(("bytes in buffer: %d\n",mbuf_bytes));
323 return B_OK;
327 /* ----------
328 midi_write - handle write() calls
329 ----- */
330 static status_t
331 midi_write(void * cookie, off_t pos, const void * data, size_t * num_bytes)
333 unsigned char *bufdata;
334 uint32 i;
335 size_t count;
337 mpu401device *mpu_device = (mpu401device *)cookie;
338 bufdata = (unsigned char*)data; /* Pointer to midi data buffer */
339 count = *num_bytes;
341 /* Only for deep debugging..will slow things down */
342 /*PRINT(("write %d bytes, addrport 0x%x, workarounds 0x%x\n",
343 (int)count, mpu_device->addrport, mpu_device->workarounds));*/
345 acquire_sem(mpu_device->writesemaphore);
346 for (i = 0; i < count; i++) {
347 // wait until device is ready
348 while ((Read_MPU401(mpu_device->addrport, UARTCMD,
349 mpu_device->workarounds) & MPU401_OK2WR));
351 Write_MPU401(mpu_device->addrport, UARTDATA,
352 mpu_device->workarounds, *(bufdata+i));
355 *num_bytes = 0;
356 release_sem(mpu_device->writesemaphore);
358 return B_OK;
362 /* ----------
363 interrupt_hook - handle interrupts for mpu401 data
364 ----- */
365 static bool
366 interrupt_hook(void * cookie)
368 mpu401device *mpu_device = (mpu401device *)cookie;
370 /* Only for deep debugging..will slow things down */
371 //PRINT(("irq! port: 0x%x\n",mpu_device->addrport));
373 /* Input data is available when bit 7 of the Status port is zero.
374 Conversely, when bit 7 is is a one, no MIDI data is available.
375 Reading from the data port will often clear the interrupt signal
376 depending on the sound card. */
378 if ((Read_MPU401(mpu_device->addrport, UARTCMD,
379 mpu_device->workarounds) & MPU401_OK2RD) == 0) {
380 /* Okay, midi data waiting to be read from device */
381 if (mbuf_current >= (MBUF_ELEMENTS-1))
382 mbuf_current = 0;
384 /* store midi data byte into buffer */
385 mpubuffer[mbuf_current] = Read_MPU401(mpu_device->addrport,
386 UARTDATA, mpu_device->workarounds);
387 mbuf_current++; /* pointer to next blank byte */
388 mbuf_bytes++; /* increment count of midi data bytes */
390 release_sem_etc(mpu_device->readsemaphore, 1, B_DO_NOT_RESCHEDULE);
392 return TRUE; //B_INVOKE_SCHEDULER
395 /* No midi data from this interrupt */
396 return FALSE; //B_UNHANDLED_INTERRUPT
400 /*-----------------------------------------------------------------*/
402 uchar
403 Read_MPU401(unsigned int addrport, const char cmdtype,
404 unsigned int workarounds)
406 uchar mpudatabyte;
407 cpu_status status;
408 unsigned int regptr;
410 /* Only for deep debugging..will slow things down */
411 //PRINT(("read workaround 0x%x\n",workarounds));
412 switch (workarounds) {
413 case 0x11020004: /* Creative Audigy Gameport */
414 regptr = (((I_MPU1 + cmdtype) << 16) & PTR_ADDRESS_MASK);
415 status = lock();
416 gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA or CMD */
417 mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
418 unlock(status);
419 break;
421 case 0x11020005: /* Creative Audigy LiveDrive */
422 regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
423 status = lock();
424 gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA2 or CMD2 */
425 mpudatabyte = gPCI->read_io_32(addrport + D_DATA);
426 unlock(status);
427 break;
429 case 0x14121712:
430 status = lock();
431 mpudatabyte = gPCI->read_io_8(addrport + cmdtype);
432 unlock(status);
433 break;
435 default:
436 mpudatabyte = gISA->read_io_8(addrport + cmdtype);
437 break;
439 return mpudatabyte;
443 status_t
444 Write_MPU401(unsigned int addrport, const char cmdtype,
445 unsigned int workarounds, uchar mpudatabyte)
447 cpu_status status;
448 unsigned int regptr;
450 /* Only for deep debugging..will slow things down */
451 //PRINT(("write workaround 0x%x at addr: 0x%x\n",workarounds,addrport));
452 switch (workarounds) {
453 case 0x11020004: /* Creative Audigy Gameport */
454 regptr = (((I_MPU1 + cmdtype) << 16) & PTR_ADDRESS_MASK);
455 status = lock();
456 gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA or CMD */
457 gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
458 unlock(status);
459 break;
461 case 0x11020005: /* Creative Audigy LiveDrive */
462 regptr = (((I_MPU2 + cmdtype) << 16) & PTR_ADDRESS_MASK);
463 status = lock();
464 gPCI->write_io_32(addrport + D_PTR, regptr); /*DATA2 or CMD2 */
465 gPCI->write_io_32(addrport + D_DATA, mpudatabyte);
466 unlock(status);
467 break;
469 case 0x14121712:
470 status = lock();
471 gPCI->write_io_8(addrport + cmdtype, mpudatabyte);
472 unlock(status);
473 break;
475 default:
476 gISA->write_io_8(addrport + cmdtype, mpudatabyte);
477 break;
479 return B_OK;
483 /*-----------------------------------------------------------------*/
485 static status_t
486 std_ops(int32 op, ...)
488 switch(op) {
490 case B_MODULE_INIT:
492 LOG_CREATE();
493 PRINT(("B_MODULE_INIT\n"));
495 if (get_module(B_ISA_MODULE_NAME, (module_info **)&gISA) < B_OK)
496 return B_ERROR;
497 if (get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI) < B_OK)
498 return B_ERROR;
499 return B_OK;
501 case B_MODULE_UNINIT:
502 put_module(B_ISA_MODULE_NAME);
503 put_module(B_PCI_MODULE_NAME);
504 PRINT(("B_MODULE_UNINIT\n"));
505 return B_OK;
507 default:
508 return B_ERROR;
512 static generic_mpu401_module mpu401_module =
515 B_MPU_401_MODULE_NAME,
516 B_KEEP_LOADED /*0*/ ,
517 std_ops
519 create_device,
520 delete_device,
521 midi_open,
522 midi_close,
523 midi_free,
524 midi_control,
525 midi_read,
526 midi_write,
527 interrupt_hook
530 // Module v2 seems to be undocumented
531 static generic_mpu401_module mpu401_module2 =
534 "generic/mpu401/v2",
536 std_ops
538 create_device_v2,
539 delete_device,
540 midi_open,
541 midi_close,
542 midi_free,
543 midi_control,
544 midi_read,
545 midi_write,
546 interrupt_hook
549 _EXPORT generic_mpu401_module *modules[] =
551 &mpu401_module,
552 &mpu401_module2,
553 NULL
556 spinlock locked = B_SPINLOCK_INITIALIZER;
557 cpu_status
558 lock(void)
560 cpu_status status = disable_interrupts();
561 acquire_spinlock(&locked);
562 return status;
565 void
566 unlock(cpu_status status)
568 release_spinlock(&locked);
569 restore_interrupts(status);