| 1 | /*- |
| 2 | * Copyright (c) 1991 The Regents of the University of California. |
| 3 | * All rights reserved. |
| 4 | * |
| 5 | * This code is derived from software contributed to Berkeley by |
| 6 | * William Jolitz. |
| 7 | * |
| 8 | * Redistribution and use in source and binary forms, with or without |
| 9 | * modification, are permitted provided that the following conditions |
| 10 | * are met: |
| 11 | * 1. Redistributions of source code must retain the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer. |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright |
| 14 | * notice, this list of conditions and the following disclaimer in the |
| 15 | * documentation and/or other materials provided with the distribution. |
| 16 | * 3. All advertising materials mentioning features or use of this software |
| 17 | * must display the following acknowledgement: |
| 18 | * This product includes software developed by the University of |
| 19 | * California, Berkeley and its contributors. |
| 20 | * 4. Neither the name of the University nor the names of its contributors |
| 21 | * may be used to endorse or promote products derived from this software |
| 22 | * without specific prior written permission. |
| 23 | * |
| 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 34 | * SUCH DAMAGE. |
| 35 | * |
| 36 | * @(#)isa.c 7.2 (Berkeley) 5/13/91 |
| 37 | */ |
| 38 | static char rcsid[] = "$Header: /usr/src/sys.386bsd/i386/isa/RCS/isa.c,v 1.2 92/01/21 14:34:23 william Exp Locker: root $"; |
| 39 | |
| 40 | /* |
| 41 | * code to manage AT bus |
| 42 | */ |
| 43 | |
| 44 | #include "param.h" |
| 45 | #include "systm.h" |
| 46 | #include "conf.h" |
| 47 | #include "file.h" |
| 48 | #include "buf.h" |
| 49 | #include "uio.h" |
| 50 | #include "syslog.h" |
| 51 | #include "malloc.h" |
| 52 | #include "rlist.h" |
| 53 | #include "machine/segments.h" |
| 54 | #include "vm/vm.h" |
| 55 | #include "i386/isa/isa_device.h" |
| 56 | #include "i386/isa/isa.h" |
| 57 | #include "i386/isa/icu.h" |
| 58 | #include "i386/isa/ic/i8237.h" |
| 59 | #include "i386/isa/ic/i8042.h" |
| 60 | |
| 61 | int config_isadev(struct isa_device *, u_short *); |
| 62 | #ifdef notyet |
| 63 | struct rlist *isa_iomem; |
| 64 | |
| 65 | /* |
| 66 | * Configure all ISA devices |
| 67 | */ |
| 68 | isa_configure() { |
| 69 | struct isa_device *dvp; |
| 70 | struct isa_driver *dp; |
| 71 | |
| 72 | splhigh(); |
| 73 | INTREN(IRQ_SLAVE); |
| 74 | /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/ |
| 75 | for (dvp = isa_devtab_tty; dvp; dvp++) |
| 76 | (void) config_isadev(dvp, &ttymask); |
| 77 | for (dvp = isa_devtab_bio; dvp; dvp++) |
| 78 | (void) config_isadev(dvp, &biomask); |
| 79 | for (dvp = isa_devtab_net; dvp; dvp++) |
| 80 | (void) config_isadev(dvp, &netmask); |
| 81 | for (dvp = isa_devtab_null; dvp; dvp++) |
| 82 | (void) config_isadev(dvp, 0); |
| 83 | #include "sl.h" |
| 84 | #if NSL > 0 |
| 85 | netmask |= ttymask; |
| 86 | ttymask |= netmask; |
| 87 | #endif |
| 88 | /* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ |
| 89 | splnone(); |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | * Configure an ISA device. |
| 94 | */ |
| 95 | config_isadev(isdp, mp) |
| 96 | struct isa_device *isdp; |
| 97 | u_short *mp; |
| 98 | { |
| 99 | struct isa_driver *dp; |
| 100 | static short drqseen, irqseen; |
| 101 | |
| 102 | if (dp = isdp->id_driver) { |
| 103 | /* if a device with i/o memory, convert to virtual address */ |
| 104 | if (isdp->id_maddr) { |
| 105 | extern unsigned int atdevbase; |
| 106 | |
| 107 | isdp->id_maddr -= IOM_BEGIN; |
| 108 | isdp->id_maddr += atdevbase; |
| 109 | } |
| 110 | isdp->id_alive = (*dp->probe)(isdp); |
| 111 | if (isdp->id_alive) { |
| 112 | |
| 113 | printf("%s%d at port 0x%x ", dp->name, |
| 114 | isdp->id_unit, isdp->id_iobase); |
| 115 | |
| 116 | /* check for conflicts */ |
| 117 | if (irqseen & isdp->id_irq) { |
| 118 | printf("INTERRUPT CONFLICT - irq%d\n", |
| 119 | ffs(isdp->id_irq) - 1); |
| 120 | return (0); |
| 121 | } |
| 122 | if (isdp->id_drq != -1 |
| 123 | && (drqseen & (1<<isdp->id_drq))) { |
| 124 | printf("DMA CONFLICT - drq%d\n", isdp->id_drq); |
| 125 | return (0); |
| 126 | } |
| 127 | /* NEED TO CHECK IOMEM CONFLICT HERE */ |
| 128 | |
| 129 | /* allocate and wire in device */ |
| 130 | if(isdp->id_irq) { |
| 131 | int intrno; |
| 132 | |
| 133 | intrno = ffs(isdp->id_irq)-1; |
| 134 | printf("irq %d ", intrno); |
| 135 | INTREN(isdp->id_irq); |
| 136 | if(mp)INTRMASK(*mp,isdp->id_irq); |
| 137 | setidt(NRSVIDT + intrno, isdp->id_intr, |
| 138 | SDT_SYS386IGT, SEL_KPL); |
| 139 | irqseen |= isdp->id_irq; |
| 140 | } |
| 141 | if (isdp->id_drq != -1) { |
| 142 | printf("drq %d ", isdp->id_drq); |
| 143 | drqseen |= 1 << isdp->id_drq; |
| 144 | } |
| 145 | |
| 146 | (*dp->attach)(isdp); |
| 147 | |
| 148 | printf("on isa\n"); |
| 149 | } |
| 150 | return (1); |
| 151 | } else return(0); |
| 152 | } |
| 153 | #else |
| 154 | /* |
| 155 | * Configure all ISA devices |
| 156 | */ |
| 157 | isa_configure() { |
| 158 | struct isa_device *dvp; |
| 159 | struct isa_driver *dp; |
| 160 | |
| 161 | splhigh(); |
| 162 | INTREN(IRQ_SLAVE); |
| 163 | for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++); |
| 164 | for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++); |
| 165 | for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++); |
| 166 | for (dvp = isa_devtab_null; config_isadev(dvp,0); dvp++); |
| 167 | #include "sl.h" |
| 168 | #if NSL > 0 |
| 169 | netmask |= ttymask; |
| 170 | ttymask |= netmask; |
| 171 | #endif |
| 172 | /* biomask |= ttymask ; can some tty devices use buffers? */ |
| 173 | /* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ |
| 174 | splnone(); |
| 175 | } |
| 176 | |
| 177 | /* |
| 178 | * Configure an ISA device. |
| 179 | */ |
| 180 | config_isadev(isdp, mp) |
| 181 | struct isa_device *isdp; |
| 182 | u_short *mp; |
| 183 | { |
| 184 | struct isa_driver *dp; |
| 185 | |
| 186 | if (dp = isdp->id_driver) { |
| 187 | if (isdp->id_maddr) { |
| 188 | extern u_int atdevbase; |
| 189 | |
| 190 | isdp->id_maddr -= 0xa0000; |
| 191 | isdp->id_maddr += atdevbase; |
| 192 | } |
| 193 | isdp->id_alive = (*dp->probe)(isdp); |
| 194 | if (isdp->id_alive) { |
| 195 | printf("%s%d", dp->name, isdp->id_unit); |
| 196 | (*dp->attach)(isdp); |
| 197 | printf(" at 0x%x ", isdp->id_iobase); |
| 198 | if(isdp->id_irq) { |
| 199 | int intrno; |
| 200 | |
| 201 | intrno = ffs(isdp->id_irq)-1; |
| 202 | printf("irq %d ", intrno); |
| 203 | INTREN(isdp->id_irq); |
| 204 | if(mp)INTRMASK(*mp,isdp->id_irq); |
| 205 | setidt(ICU_OFFSET+intrno, isdp->id_intr, |
| 206 | SDT_SYS386IGT, SEL_KPL); |
| 207 | } |
| 208 | if (isdp->id_drq != -1) printf("drq %d ", isdp->id_drq); |
| 209 | printf("on isa\n"); |
| 210 | } |
| 211 | return (1); |
| 212 | } else return(0); |
| 213 | } |
| 214 | #endif |
| 215 | |
| 216 | #define IDTVEC(name) __CONCAT(X,name) |
| 217 | /* default interrupt vector table entries */ |
| 218 | extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3), |
| 219 | IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7), |
| 220 | IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), |
| 221 | IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); |
| 222 | |
| 223 | static *defvec[16] = { |
| 224 | &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), |
| 225 | &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), |
| 226 | &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), |
| 227 | &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; |
| 228 | |
| 229 | /* out of range default interrupt vector gate entry */ |
| 230 | extern IDTVEC(intrdefault); |
| 231 | |
| 232 | /* |
| 233 | * Fill in default interrupt table (in case of spuruious interrupt |
| 234 | * during configuration of kernel, setup interrupt control unit |
| 235 | */ |
| 236 | isa_defaultirq() { |
| 237 | int i; |
| 238 | |
| 239 | /* icu vectors */ |
| 240 | for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) |
| 241 | setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); |
| 242 | |
| 243 | /* out of range vectors */ |
| 244 | for (i = NRSVIDT; i < NIDT; i++) |
| 245 | setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); |
| 246 | |
| 247 | /* clear npx intr latch */ |
| 248 | outb(0xf1,0); |
| 249 | |
| 250 | /* initialize 8259's */ |
| 251 | outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ |
| 252 | outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ |
| 253 | outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ |
| 254 | outb(IO_ICU1+1, 1); /* 8086 mode */ |
| 255 | outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ |
| 256 | outb(IO_ICU1, 2); /* default to ISR on read */ |
| 257 | |
| 258 | outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ |
| 259 | outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ |
| 260 | outb(IO_ICU2+1,2); /* my slave id is 2 */ |
| 261 | outb(IO_ICU2+1,1); /* 8086 mode */ |
| 262 | outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ |
| 263 | outb(IO_ICU2, 2); /* default to ISR on read */ |
| 264 | } |
| 265 | |
| 266 | /* region of physical memory known to be contiguous */ |
| 267 | vm_offset_t isaphysmem; |
| 268 | static caddr_t dma_bounce[8]; /* XXX */ |
| 269 | static char bounced[8]; /* XXX */ |
| 270 | #define MAXDMASZ 512 /* XXX */ |
| 271 | |
| 272 | /* high byte of address is stored in this port for i-th dma channel */ |
| 273 | static short dmapageport[8] = |
| 274 | { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; |
| 275 | |
| 276 | /* |
| 277 | * isa_dmacascade(): program 8237 DMA controller channel to accept |
| 278 | * external dma control by a board. |
| 279 | */ |
| 280 | void isa_dmacascade(unsigned chan) |
| 281 | { int modeport; |
| 282 | |
| 283 | if (chan > 7) |
| 284 | panic("isa_dmacascade: impossible request"); |
| 285 | |
| 286 | /* set dma channel mode, and set dma channel mode */ |
| 287 | if ((chan & 4) == 0) |
| 288 | modeport = IO_DMA1 + 0xb; |
| 289 | else |
| 290 | modeport = IO_DMA2 + 0x16; |
| 291 | outb(modeport, DMA37MD_CASCADE | (chan & 3)); |
| 292 | if ((chan & 4) == 0) |
| 293 | outb(modeport - 1, chan & 3); |
| 294 | else |
| 295 | outb(modeport - 2, chan & 3); |
| 296 | } |
| 297 | |
| 298 | /* |
| 299 | * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment |
| 300 | * problems by using a bounce buffer. |
| 301 | */ |
| 302 | void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan) |
| 303 | { vm_offset_t phys; |
| 304 | int modeport, waport, mskport; |
| 305 | caddr_t newaddr; |
| 306 | |
| 307 | if (chan > 7 || nbytes > (1<<16)) |
| 308 | panic("isa_dmastart: impossible request"); |
| 309 | |
| 310 | if (isa_dmarangecheck(addr, nbytes)) { |
| 311 | if (dma_bounce[chan] == 0) |
| 312 | dma_bounce[chan] = |
| 313 | /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ |
| 314 | (caddr_t) isaphysmem + NBPG*chan; |
| 315 | bounced[chan] = 1; |
| 316 | newaddr = dma_bounce[chan]; |
| 317 | *(int *) newaddr = 0; /* XXX */ |
| 318 | |
| 319 | /* copy bounce buffer on write */ |
| 320 | if (!(flags & B_READ)) |
| 321 | bcopy(addr, newaddr, nbytes); |
| 322 | addr = newaddr; |
| 323 | } |
| 324 | |
| 325 | /* translate to physical */ |
| 326 | phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); |
| 327 | |
| 328 | /* set dma channel mode, and reset address ff */ |
| 329 | if ((chan & 4) == 0) |
| 330 | modeport = IO_DMA1 + 0xb; |
| 331 | else |
| 332 | modeport = IO_DMA2 + 0x16; |
| 333 | if (flags & B_READ) |
| 334 | outb(modeport, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); |
| 335 | else |
| 336 | outb(modeport, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); |
| 337 | if ((chan & 4) == 0) |
| 338 | outb(modeport + 1, 0); |
| 339 | else |
| 340 | outb(modeport + 2, 0); |
| 341 | |
| 342 | /* send start address */ |
| 343 | if ((chan & 4) == 0) { |
| 344 | waport = IO_DMA1 + (chan<<1); |
| 345 | outb(waport, phys); |
| 346 | outb(waport, phys>>8); |
| 347 | } else { |
| 348 | waport = IO_DMA2 + ((chan - 4)<<2); |
| 349 | outb(waport, phys>>1); |
| 350 | outb(waport, phys>>9); |
| 351 | } |
| 352 | outb(dmapageport[chan], phys>>16); |
| 353 | |
| 354 | /* send count */ |
| 355 | if ((chan & 4) == 0) { |
| 356 | outb(waport + 1, --nbytes); |
| 357 | outb(waport + 1, nbytes>>8); |
| 358 | } else { |
| 359 | nbytes <<= 1; |
| 360 | outb(waport + 2, --nbytes); |
| 361 | outb(waport + 2, nbytes>>8); |
| 362 | } |
| 363 | |
| 364 | /* unmask channel */ |
| 365 | if ((chan & 4) == 0) |
| 366 | mskport = IO_DMA1 + 0x0a; |
| 367 | else |
| 368 | mskport = IO_DMA2 + 0x14; |
| 369 | outb(mskport, chan & 3); |
| 370 | } |
| 371 | |
| 372 | void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) |
| 373 | { |
| 374 | |
| 375 | /* copy bounce buffer on read */ |
| 376 | /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/ |
| 377 | if (bounced[chan]) { |
| 378 | bcopy(dma_bounce[chan], addr, nbytes); |
| 379 | bounced[chan] = 0; |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | /* |
| 384 | * Check for problems with the address range of a DMA transfer |
| 385 | * (non-contiguous physical pages, outside of bus address space). |
| 386 | * Return true if special handling needed. |
| 387 | */ |
| 388 | |
| 389 | isa_dmarangecheck(caddr_t va, unsigned length) { |
| 390 | vm_offset_t phys, priorpage, endva; |
| 391 | |
| 392 | endva = (vm_offset_t)round_page(va + length); |
| 393 | for (; va < (caddr_t) endva ; va += NBPG) { |
| 394 | phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); |
| 395 | #define ISARAM_END RAM_END |
| 396 | if (phys == 0) |
| 397 | panic("isa_dmacheck: no physical page present"); |
| 398 | if (phys > ISARAM_END) |
| 399 | return (1); |
| 400 | if (priorpage && priorpage + NBPG != phys) |
| 401 | return (1); |
| 402 | priorpage = phys; |
| 403 | } |
| 404 | return (0); |
| 405 | } |
| 406 | |
| 407 | /* head of queue waiting for physmem to become available */ |
| 408 | struct buf isa_physmemq; |
| 409 | |
| 410 | /* blocked waiting for resource to become free for exclusive use */ |
| 411 | static isaphysmemflag; |
| 412 | /* if waited for and call requested when free (B_CALL) */ |
| 413 | static void (*isaphysmemunblock)(); /* needs to be a list */ |
| 414 | |
| 415 | /* |
| 416 | * Allocate contiguous physical memory for transfer, returning |
| 417 | * a *virtual* address to region. May block waiting for resource. |
| 418 | * (assumed to be called at splbio()) |
| 419 | */ |
| 420 | caddr_t |
| 421 | isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { |
| 422 | |
| 423 | isaphysmemunblock = func; |
| 424 | while (isaphysmemflag & B_BUSY) { |
| 425 | isaphysmemflag |= B_WANTED; |
| 426 | sleep(&isaphysmemflag, PRIBIO); |
| 427 | } |
| 428 | isaphysmemflag |= B_BUSY; |
| 429 | |
| 430 | return((caddr_t)isaphysmem); |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * Free contiguous physical memory used for transfer. |
| 435 | * (assumed to be called at splbio()) |
| 436 | */ |
| 437 | void |
| 438 | isa_freephysmem(caddr_t va, unsigned length) { |
| 439 | |
| 440 | isaphysmemflag &= ~B_BUSY; |
| 441 | if (isaphysmemflag & B_WANTED) { |
| 442 | isaphysmemflag &= B_WANTED; |
| 443 | wakeup(&isaphysmemflag); |
| 444 | if (isaphysmemunblock) |
| 445 | (*isaphysmemunblock)(); |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | /* |
| 450 | * Handle a NMI, possibly a machine check. |
| 451 | * return true to panic system, false to ignore. |
| 452 | */ |
| 453 | isa_nmi(cd) { |
| 454 | |
| 455 | log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); |
| 456 | return(0); |
| 457 | } |
| 458 | |
| 459 | /* |
| 460 | * Caught a stray interrupt, notify |
| 461 | */ |
| 462 | isa_strayintr(d) { |
| 463 | |
| 464 | #ifdef notdef |
| 465 | /* DON'T BOTHER FOR NOW! */ |
| 466 | /* for some reason, we get bursts of intr #7, even if not enabled! */ |
| 467 | log(LOG_ERR,"ISA strayintr %x", d); |
| 468 | #endif |
| 469 | } |
| 470 | |
| 471 | /* |
| 472 | * Wait "n" microseconds. Relies on timer 0 to have 1Mhz clock, regardless |
| 473 | * of processor board speed. Note: timer had better have been programmed |
| 474 | * before this is first used! |
| 475 | */ |
| 476 | DELAY(n) { |
| 477 | int tick = getit(0,0) & 1; |
| 478 | |
| 479 | while (n--) { |
| 480 | /* wait approximately 1 micro second */ |
| 481 | while (tick == getit(0,0) & 1) ; |
| 482 | |
| 483 | tick = getit(0,0) & 1; |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | getit(unit, timer) { |
| 488 | int port = (unit ? IO_TIMER2 : IO_TIMER1) + timer, val; |
| 489 | |
| 490 | val = inb(port); |
| 491 | val = (inb(port) << 8) + val; |
| 492 | return (val); |
| 493 | } |
| 494 | |
| 495 | extern int hz; |
| 496 | |
| 497 | static beeping; |
| 498 | static |
| 499 | sysbeepstop(f) |
| 500 | { |
| 501 | /* disable counter 2 */ |
| 502 | outb(0x61, inb(0x61) & 0xFC); |
| 503 | if (f) |
| 504 | timeout(sysbeepstop, 0, f); |
| 505 | else |
| 506 | beeping = 0; |
| 507 | } |
| 508 | |
| 509 | void sysbeep(int pitch, int period) |
| 510 | { |
| 511 | |
| 512 | outb(0x61, inb(0x61) | 3); /* enable counter 2 */ |
| 513 | outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ |
| 514 | |
| 515 | outb(0x42, pitch); |
| 516 | outb(0x42, (pitch>>8)); |
| 517 | |
| 518 | if (!beeping) { |
| 519 | beeping = period; |
| 520 | timeout(sysbeepstop, period/2, period); |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | /* |
| 525 | * Pass command to keyboard controller (8042) |
| 526 | */ |
| 527 | unsigned kbc_8042cmd(val) { |
| 528 | |
| 529 | while (inb(KBSTATP)&KBS_IBF); |
| 530 | if (val) outb(KBCMDP, val); |
| 531 | while (inb(KBSTATP)&KBS_IBF); |
| 532 | return (inb(KBDATAP)); |
| 533 | } |