cuda_adb.c

00001 /*
00002  * Copyright (c) 2010 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <sys/types.h>
00042 #include <bool.h>
00043 #include <ddi.h>
00044 #include <libarch/ddi.h>
00045 #include <devmap.h>
00046 #include <sysinfo.h>
00047 #include <errno.h>
00048 #include <ipc/adb.h>
00049 #include <async.h>
00050 #include <assert.h>
00051 #include "cuda_adb.h"
00052 
00053 #define NAME "cuda_adb"
00054 
00055 static void cuda_connection(ipc_callid_t iid, ipc_call_t *icall);
00056 static int cuda_init(void);
00057 static void cuda_irq_handler(ipc_callid_t iid, ipc_call_t *call);
00058 
00059 static void cuda_irq_listen(void);
00060 static void cuda_irq_receive(void);
00061 static void cuda_irq_rcv_end(void *buf, size_t *len);
00062 static void cuda_irq_send_start(void);
00063 static void cuda_irq_send(void);
00064 
00065 static void cuda_packet_handle(uint8_t *buf, size_t len);
00066 static void cuda_send_start(void);
00067 static void cuda_autopoll_set(bool enable);
00068 
00069 static void adb_packet_handle(uint8_t *data, size_t size, bool autopoll);
00070 
00071 
00073 enum {
00074         TREQ    = 0x08,
00075         TACK    = 0x10,
00076         TIP     = 0x20
00077 };
00078 
00080 enum {
00081         IER_CLR = 0x00,
00082         IER_SET = 0x80,
00083 
00084         SR_INT  = 0x04,
00085         ALL_INT = 0x7f
00086 };
00087 
00089 enum {
00090         SR_OUT  = 0x10
00091 };
00092 
00094 enum {
00095         PT_ADB  = 0x00,
00096         PT_CUDA = 0x01
00097 };
00098 
00100 enum {
00101         CPT_AUTOPOLL    = 0x01
00102 };
00103 
00104 enum {
00105         ADB_MAX_ADDR    = 16
00106 };
00107 
00108 static irq_cmd_t cuda_cmds[] = {
00109         {
00110                 .cmd = CMD_PIO_READ_8,
00111                 .addr = NULL,   /* will be patched in run-time */
00112                 .dstarg = 1
00113         },
00114         {
00115                 .cmd = CMD_BTEST,
00116                 .value = SR_INT,
00117                 .srcarg = 1,
00118                 .dstarg = 2
00119         },
00120         {
00121                 .cmd = CMD_PREDICATE,
00122                 .value = 1,
00123                 .srcarg = 2
00124         },
00125         {
00126                 .cmd = CMD_ACCEPT
00127         }
00128 };
00129 
00130 
00131 static irq_code_t cuda_irq_code = {
00132         sizeof(cuda_cmds) / sizeof(irq_cmd_t),
00133         cuda_cmds
00134 };
00135 
00136 static cuda_instance_t cinst;
00137 
00138 static cuda_instance_t *instance = &cinst;
00139 static cuda_t *dev;
00140 
00141 static adb_dev_t adb_dev[ADB_MAX_ADDR];
00142 
00143 int main(int argc, char *argv[])
00144 {
00145         devmap_handle_t devmap_handle;
00146         int rc;
00147         int i;
00148 
00149         printf(NAME ": VIA-CUDA Apple Desktop Bus driver\n");
00150 
00151         for (i = 0; i < ADB_MAX_ADDR; ++i) {
00152                 adb_dev[i].client_phone = -1;
00153                 adb_dev[i].devmap_handle = 0;
00154         }
00155 
00156         rc = devmap_driver_register(NAME, cuda_connection);
00157         if (rc < 0) {
00158                 printf(NAME ": Unable to register driver.\n");
00159                 return rc;
00160         }
00161 
00162         rc = devmap_device_register("adb/kbd", &devmap_handle);
00163         if (rc != EOK) {
00164                 printf(NAME ": Unable to register device %s.\n", "adb/kdb");
00165                 return rc;
00166         }
00167 
00168         adb_dev[2].devmap_handle = devmap_handle;
00169         adb_dev[8].devmap_handle = devmap_handle;
00170 
00171         rc = devmap_device_register("adb/mouse", &devmap_handle);
00172         if (rc != EOK) {
00173                 printf(NAME ": Unable to register device %s.\n", "adb/mouse");
00174                 return rc;
00175         }
00176 
00177         adb_dev[9].devmap_handle = devmap_handle;
00178 
00179         if (cuda_init() < 0) {
00180                 printf("cuda_init() failed\n");
00181                 return 1;
00182         }
00183 
00184         task_retval(0);
00185         async_manager();
00186 
00187         return 0;
00188 }
00189 
00191 static void cuda_connection(ipc_callid_t iid, ipc_call_t *icall)
00192 {
00193         ipc_callid_t callid;
00194         ipc_call_t call;
00195         sysarg_t method;
00196         devmap_handle_t dh;
00197         int retval;
00198         int dev_addr, i;
00199 
00200         /* Get the device handle. */
00201         dh = IPC_GET_ARG1(*icall);
00202 
00203         /* Determine which disk device is the client connecting to. */
00204         dev_addr = -1;
00205         for (i = 0; i < ADB_MAX_ADDR; i++) {
00206                 if (adb_dev[i].devmap_handle == dh)
00207                         dev_addr = i;
00208         }
00209 
00210         if (dev_addr < 0) {
00211                 async_answer_0(iid, EINVAL);
00212                 return;
00213         }
00214 
00215         /* Answer the IPC_M_CONNECT_ME_TO call. */
00216         async_answer_0(iid, EOK);
00217 
00218         while (1) {
00219                 callid = async_get_call(&call);
00220                 method = IPC_GET_IMETHOD(call);
00221                 switch (method) {
00222                 case IPC_M_PHONE_HUNGUP:
00223                         /* The other side has hung up. */
00224                         async_answer_0(callid, EOK);
00225                         return;
00226                 case IPC_M_CONNECT_TO_ME:
00227                         if (adb_dev[dev_addr].client_phone != -1) {
00228                                 retval = ELIMIT;
00229                                 break;
00230                         }
00231                         adb_dev[dev_addr].client_phone = IPC_GET_ARG5(call);
00232                         /*
00233                          * A hack so that we send the data to the phone
00234                          * regardless of which address the device is on.
00235                          */
00236                         for (i = 0; i < ADB_MAX_ADDR; ++i) {
00237                                 if (adb_dev[i].devmap_handle == dh) {
00238                                         adb_dev[i].client_phone = IPC_GET_ARG5(call);
00239                                 }
00240                         }
00241                         retval = 0;
00242                         break;
00243                 default:
00244                         retval = EINVAL;
00245                         break;
00246                 }
00247                 async_answer_0(callid, retval);
00248         }
00249 }
00250 
00251 
00252 static int cuda_init(void)
00253 {
00254         if (sysinfo_get_value("cuda.address.physical", &(instance->cuda_physical)) != EOK)
00255                 return -1;
00256         
00257         if (sysinfo_get_value("cuda.address.kernel", &(instance->cuda_kernel)) != EOK)
00258                 return -1;
00259         
00260         void *vaddr;
00261         if (pio_enable((void *) instance->cuda_physical, sizeof(cuda_t), &vaddr) != 0)
00262                 return -1;
00263         
00264         dev = vaddr;
00265 
00266         instance->cuda = dev;
00267         instance->xstate = cx_listen;
00268         instance->bidx = 0;
00269         instance->snd_bytes = 0;
00270 
00271         fibril_mutex_initialize(&instance->dev_lock);
00272 
00273         /* Disable all interrupts from CUDA. */
00274         pio_write_8(&dev->ier, IER_CLR | ALL_INT);
00275 
00276         cuda_irq_code.cmds[0].addr = (void *) &((cuda_t *) instance->cuda_kernel)->ifr;
00277         async_set_interrupt_received(cuda_irq_handler);
00278         register_irq(10, device_assign_devno(), 0, &cuda_irq_code);
00279 
00280         /* Enable SR interrupt. */
00281         pio_write_8(&dev->ier, TIP | TREQ);
00282         pio_write_8(&dev->ier, IER_SET | SR_INT);
00283 
00284         /* Enable ADB autopolling. */
00285         cuda_autopoll_set(true);
00286 
00287         return 0;
00288 }
00289 
00290 static void cuda_irq_handler(ipc_callid_t iid, ipc_call_t *call)
00291 {
00292         uint8_t rbuf[CUDA_RCV_BUF_SIZE];
00293         size_t len;
00294         bool handle;
00295 
00296         handle = false;
00297         len = 0;
00298 
00299         fibril_mutex_lock(&instance->dev_lock);
00300 
00301         /* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
00302         pio_write_8(&instance->cuda->ifr, SR_INT);
00303 
00304         switch (instance->xstate) {
00305         case cx_listen: cuda_irq_listen(); break;
00306         case cx_receive: cuda_irq_receive(); break;
00307         case cx_rcv_end: cuda_irq_rcv_end(rbuf, &len);
00308             handle = true; break;
00309         case cx_send_start: cuda_irq_send_start(); break;
00310         case cx_send: cuda_irq_send(); break;
00311         }
00312 
00313         fibril_mutex_unlock(&instance->dev_lock);
00314 
00315         /* Handle an incoming packet. */
00316         if (handle)
00317                 cuda_packet_handle(rbuf, len);
00318 }
00319 
00324 static void cuda_irq_listen(void)
00325 {
00326         uint8_t b;
00327 
00328         b = pio_read_8(&dev->b);
00329 
00330         if ((b & TREQ) != 0) {
00331                 printf("cuda_irq_listen: no TREQ?!\n");
00332                 return;
00333         }
00334 
00335         pio_read_8(&dev->sr);
00336         pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
00337         instance->xstate = cx_receive;
00338 }
00339 
00344 static void cuda_irq_receive(void)
00345 {
00346         uint8_t b, data;
00347 
00348         data = pio_read_8(&dev->sr);
00349         if (instance->bidx < CUDA_RCV_BUF_SIZE)
00350                 instance->rcv_buf[instance->bidx++] = data;
00351 
00352         b = pio_read_8(&dev->b);
00353 
00354         if ((b & TREQ) == 0) {
00355                 pio_write_8(&dev->b, b ^ TACK);
00356         } else {
00357                 pio_write_8(&dev->b, b | TACK | TIP);
00358                 instance->xstate = cx_rcv_end;
00359         }
00360 }
00361 
00367 static void cuda_irq_rcv_end(void *buf, size_t *len)
00368 {
00369         uint8_t b;
00370         
00371         b = pio_read_8(&dev->b);
00372         pio_read_8(&dev->sr);
00373         
00374         if ((b & TREQ) == 0) {
00375                 instance->xstate = cx_receive;
00376                 pio_write_8(&dev->b, b & ~TIP);
00377         } else {
00378                 instance->xstate = cx_listen;
00379                 cuda_send_start();
00380         }
00381         
00382         memcpy(buf, instance->rcv_buf, instance->bidx);
00383         *len = instance->bidx;
00384         instance->bidx = 0;
00385 }
00386 
00391 static void cuda_irq_send_start(void)
00392 {
00393         uint8_t b;
00394 
00395         b = pio_read_8(&dev->b);
00396 
00397         if ((b & TREQ) == 0) {
00398                 /* Collision */
00399                 pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
00400                 pio_read_8(&dev->sr);
00401                 pio_write_8(&dev->b, pio_read_8(&dev->b) | TIP | TACK);
00402                 instance->xstate = cx_listen;
00403                 return;
00404         }
00405 
00406         pio_write_8(&dev->sr, instance->snd_buf[1]);
00407         pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
00408         instance->bidx = 2;
00409 
00410         instance->xstate = cx_send;
00411 }
00412 
00417 static void cuda_irq_send(void)
00418 {
00419         if (instance->bidx < instance->snd_bytes) {
00420                 /* Send next byte. */
00421                 pio_write_8(&dev->sr, instance->snd_buf[instance->bidx++]);
00422                 pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
00423                 return;
00424         }
00425 
00426         /* End transfer. */
00427         instance->snd_bytes = 0;
00428         instance->bidx = 0;
00429 
00430         pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
00431         pio_read_8(&dev->sr);
00432         pio_write_8(&dev->b, pio_read_8(&dev->b) | TACK | TIP);
00433 
00434         instance->xstate = cx_listen;
00435         /* TODO: Match reply with request. */
00436 }
00437 
00438 static void cuda_packet_handle(uint8_t *data, size_t len)
00439 {
00440         if (data[0] != PT_ADB)
00441                 return;
00442         if (len < 2)
00443                 return;
00444 
00445         adb_packet_handle(data + 2, len - 2, (data[1] & 0x40) != 0);
00446 }
00447 
00448 static void adb_packet_handle(uint8_t *data, size_t size, bool autopoll)
00449 {
00450         uint8_t dev_addr;
00451         uint8_t reg_no;
00452         uint16_t reg_val;
00453         unsigned i;
00454 
00455         dev_addr = data[0] >> 4;
00456         reg_no = data[0] & 0x03;
00457 
00458         if (size != 3) {
00459                 printf("unrecognized packet, size=%d\n", size);
00460                 for (i = 0; i < size; ++i) {
00461                         printf(" 0x%02x", data[i]);
00462                 }
00463                 putchar('\n');
00464                 return;
00465         }
00466 
00467         if (reg_no != 0) {
00468                 printf("unrecognized packet, size=%d\n", size);
00469                 for (i = 0; i < size; ++i) {
00470                         printf(" 0x%02x", data[i]);
00471                 }
00472                 putchar('\n');
00473                 return;
00474         }
00475 
00476         reg_val = ((uint16_t) data[1] << 8) | (uint16_t) data[2];
00477 
00478         if (adb_dev[dev_addr].client_phone == -1)
00479                 return;
00480 
00481         async_msg_1(adb_dev[dev_addr].client_phone, ADB_REG_NOTIF, reg_val);
00482 }
00483 
00484 static void cuda_autopoll_set(bool enable)
00485 {
00486         instance->snd_buf[0] = PT_CUDA;
00487         instance->snd_buf[1] = CPT_AUTOPOLL;
00488         instance->snd_buf[2] = enable ? 0x01 : 0x00;
00489         instance->snd_bytes = 3;
00490         instance->bidx = 0;
00491 
00492         cuda_send_start();
00493 }
00494 
00495 static void cuda_send_start(void)
00496 {
00497         cuda_t *dev = instance->cuda;
00498 
00499         assert(instance->xstate == cx_listen);
00500 
00501         if (instance->snd_bytes == 0)
00502                 return;
00503 
00504         /* Check for incoming data. */
00505         if ((pio_read_8(&dev->b) & TREQ) == 0)
00506                 return;
00507 
00508         pio_write_8(&dev->acr, pio_read_8(&dev->acr) | SR_OUT);
00509         pio_write_8(&dev->sr, instance->snd_buf[0]);
00510         pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
00511 
00512         instance->xstate = cx_send_start;
00513 }
00514 
00515 

Generated on Thu Jun 2 07:45:50 2011 for HelenOS/USB by  doxygen 1.4.7