diff --git a/vl.c b/vl.c index e8410a8..395fe46 100644 --- a/vl.c +++ b/vl.c @@ -163,6 +163,9 @@ /* Max number of USB devices that can be specified on the commandline. */ #define MAX_USB_CMDLINE 8 +/* Max number of bluetooth switches on the commandline. */ +#define MAX_BT_CMDLINE 10 + /* XXX: use a two level table to limit memory usage */ #define MAX_IOPORTS 65536 @@ -5409,6 +5412,144 @@ struct HCIInfo *qemu_next_hci(void) return hci_table[cur_hci++]; } +static struct HCIInfo *hci_init(const char *str) +{ + char *endp; + struct bt_scatternet_s *vlan = 0; + + if (!strcmp(str, "null")) + /* null */ + return &null_hci; + else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) + /* host[:hciN] */ + return bt_host_hci(str[4] ? str + 5 : "hci0"); + else if (!strncmp(str, "hci", 3)) { + /* hci[,vlan=n] */ + if (str[3]) { + if (!strncmp(str + 3, ",vlan=", 6)) { + vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); + if (*endp) + vlan = 0; + } + } else + vlan = qemu_find_bt_vlan(0); + if (vlan) + return bt_new_hci(vlan); + } + + fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); + + return 0; +} + +static int bt_hci_parse(const char *str) +{ + struct HCIInfo *hci; + bdaddr_t bdaddr; + + if (nb_hcis >= MAX_NICS) { + fprintf(stderr, "qemu: Too many bluetooth HCIs (max %i).\n", MAX_NICS); + return -1; + } + + hci = hci_init(str); + if (!hci) + return -1; + + bdaddr.b[0] = 0x52; + bdaddr.b[1] = 0x54; + bdaddr.b[2] = 0x00; + bdaddr.b[3] = 0x12; + bdaddr.b[4] = 0x34; + bdaddr.b[5] = 0x56 + nb_hcis; + hci->bdaddr_set(hci, bdaddr.b); + + hci_table[nb_hcis++] = hci; + + return 0; +} + +static void bt_vhci_add(int vlan_id) +{ + struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); + + if (!vlan->slave) + fprintf(stderr, "qemu: warning: adding a VHCI to " + "an empty scatternet %i\n", vlan_id); + + bt_vhci_init(bt_new_hci(vlan)); +} + +static struct bt_device_s *bt_device_add(const char *opt) +{ + struct bt_scatternet_s *vlan; + int vlan_id = 0; + char *endp = strstr(opt, ",vlan="); + int len = (endp ? endp - opt : strlen(opt)) + 1; + char devname[10]; + + pstrcpy(devname, MIN(sizeof(devname), len), opt); + + if (endp) { + vlan_id = strtol(endp + 6, &endp, 0); + if (*endp) { + fprintf(stderr, "qemu: unrecognised bluetooth vlan Id\n"); + return 0; + } + } + + vlan = qemu_find_bt_vlan(vlan_id); + + if (!vlan->slave) + fprintf(stderr, "qemu: warning: adding a slave device to " + "an empty scatternet %i\n", vlan_id); + + if (!strcmp(devname, "keyboard")) + return bt_keyboard_init(vlan); + + fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname); + return 0; +} + +static int bt_parse(const char *opt) +{ + const char *endp, *p; + int vlan; + + if (strstart(opt, "hci", &endp)) { + if (!*endp || *endp == ',') { + if (*endp) + if (!strstart(endp, ",vlan=", 0)) + opt = endp + 1; + + return bt_hci_parse(opt); + } + } else if (strstart(opt, "vhci", &endp)) { + if (!*endp || *endp == ',') { + if (*endp) { + if (strstart(endp, ",vlan=", &p)) { + vlan = strtol(p, (char **) &endp, 0); + if (*endp) { + fprintf(stderr, "qemu: bad scatternet '%s'\n", p); + return 1; + } + } else { + fprintf(stderr, "qemu: bad parameter '%s'\n", endp + 1); + return 1; + } + } else + vlan = 0; + + bt_vhci_add(vlan); + return 0; + } + } else if (strstart(opt, "device:", &endp)) + return !bt_device_add(endp); + + fprintf(stderr, "qemu: bad bluetooth parameter '%s'\n", opt); + return 1; +} + /***********************************************************/ /* QEMU Block devices */ @@ -5859,6 +6000,9 @@ static int usb_device_add(const char *devname) return -1; nd_table[nic].model = "usb"; dev = usb_net_init(&nd_table[nic]); + } else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { + dev = usb_bt_init(devname[2] ? hci_init(p) : + bt_new_hci(qemu_find_bt_vlan(0))); } else { return -1; } @@ -8146,6 +8290,16 @@ static void help(int exitcode) "-net none use it alone to have zero network devices; if no -net option\n" " is provided, the default is '-net nic -net user'\n" "\n" + "-bt hci,null Dumb bluetooth HCI - doesn't respond to commands\n" + "-bt hci,host[:id]\n" + " Use host's HCI with the given name\n" + "-bt hci[,vlan=n]\n" + " Emulate a standard HCI in virtual scatternet 'n'\n" + "-bt vhci[,vlan=n]\n" + " Add host computer to virtual scatternet 'n' using VHCI\n" + "-bt device:dev[,vlan=n]\n" + " Emulate a bluetooth device 'dev' in scatternet 'n'\n" + "\n" #ifdef CONFIG_SLIRP "-tftp dir allow tftp access to files in dir [-net user]\n" "-bootp file advertise file in BOOTP replies\n" @@ -8254,6 +8408,7 @@ enum { QEMU_OPTION_bootp, QEMU_OPTION_smb, QEMU_OPTION_redir, + QEMU_OPTION_bt, QEMU_OPTION_kernel, QEMU_OPTION_append, @@ -8351,6 +8506,7 @@ static const QEMUOption qemu_options[] = { #endif { "redir", HAS_ARG, QEMU_OPTION_redir }, #endif + { "bt", HAS_ARG, QEMU_OPTION_bt }, { "kernel", HAS_ARG, QEMU_OPTION_kernel }, { "append", HAS_ARG, QEMU_OPTION_append }, @@ -8688,6 +8844,8 @@ int main(int argc, char **argv) int cyls, heads, secs, translation; const char *net_clients[MAX_NET_CLIENTS]; int nb_net_clients; + const char *bt_opts[MAX_BT_CMDLINE]; + int nb_bt_opts; int hda_index; int optind; const char *r, *optarg; @@ -8771,6 +8929,7 @@ int main(int argc, char **argv) usb_devices_index = 0; nb_net_clients = 0; + nb_bt_opts = 0; nb_drives = 0; nb_drives_opt = 0; hda_index = -1; @@ -9006,6 +9165,13 @@ int main(int argc, char **argv) net_slirp_redir(optarg); break; #endif + case QEMU_OPTION_bt: + if (nb_bt_opts >= MAX_BT_CMDLINE) { + fprintf(stderr, "qemu: too many bluetooth options\n"); + exit(1); + } + bt_opts[nb_bt_opts++] = optarg; + break; #ifdef HAS_AUDIO case QEMU_OPTION_audio_help: AUD_help (); @@ -9482,6 +9648,11 @@ int main(int argc, char **argv) } #endif + /* init the bluetooth world */ + for (i = 0; i < nb_bt_opts; i++) + if (bt_parse(bt_opts[i])) + exit(1); + /* init the memory */ phys_ram_size = machine->ram_require & ~RAMSIZE_FIXED;