From: Keith Packard Date: Tue, 19 Apr 2011 21:06:39 +0000 (-0700) Subject: Merge branch 'telemini' into telebt X-Git-Tag: 0.9.3~50 X-Git-Url: https://git.gag.com/?a=commitdiff_plain;h=44fb71ca3e5bccd5f601fc5a2d5da7292050b1d6;hp=c269e263a6accd815ed5d08c0f5a6c3d5b9d3853;p=fw%2Faltos Merge branch 'telemini' into telebt --- diff --git a/altosui/Altos.java b/altosui/Altos.java index 54aced32..6a2d9b9b 100644 --- a/altosui/Altos.java +++ b/altosui/Altos.java @@ -21,6 +21,8 @@ import java.awt.*; import java.util.*; import java.text.*; +import libaltosJNI.*; + public class Altos { /* EEProm command letters */ static final int AO_LOG_FLIGHT = 'F'; @@ -222,4 +224,91 @@ public class Altos { input = input.substring(0,dot); return input.concat(extension); } + + static public boolean initialized = false; + static public boolean loaded_library = false; + + public static boolean load_library() { + if (!initialized) { + try { + System.loadLibrary("altos"); + libaltos.altos_init(); + loaded_library = true; + } catch (UnsatisfiedLinkError e) { + loaded_library = false; + } + initialized = true; + } + return loaded_library; + } + + static int usb_vendor_altusmetrum() { + if (load_library()) + return libaltosConstants.USB_VENDOR_ALTUSMETRUM; + return 0x000a; + } + + static int usb_product_altusmetrum() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM; + return 0x000a; + } + + static int usb_product_altusmetrum_min() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MIN; + return 0x000a; + } + + static int usb_product_altusmetrum_max() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MAX; + return 0x000d; + } + + static int usb_product_telemetrum() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELEMETRUM; + return 0x000b; + } + + static int usb_product_teledongle() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELEDONGLE; + return 0x000c; + } + + static int usb_product_teleterra() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELETERRA; + return 0x000d; + } + + static int usb_product_telebt() { + if (load_library()) + return libaltosConstants.USB_PRODUCT_TELEBT; + return 0x000e; + } + + public final static int vendor_altusmetrum = usb_vendor_altusmetrum(); + public final static int product_altusmetrum = usb_product_altusmetrum(); + public final static int product_telemetrum = usb_product_telemetrum(); + public final static int product_teledongle = usb_product_teledongle(); + public final static int product_teleterra = usb_product_teleterra(); + public final static int product_telebt = usb_product_telebt(); + public final static int product_altusmetrum_min = usb_product_altusmetrum_min(); + public final static int product_altusmetrum_max = usb_product_altusmetrum_max(); + + public final static int product_any = 0x10000; + public final static int product_basestation = 0x10000 + 1; + + static String bt_product_telebt() { + if (load_library()) + return libaltosConstants.BLUETOOTH_PRODUCT_TELEBT; + return "TeleBT"; + } + + public final static String bt_product_telebt = bt_product_telebt(); + + public static AltosBTKnown bt_known = new AltosBTKnown(); } diff --git a/altosui/AltosBTDevice.java b/altosui/AltosBTDevice.java new file mode 100644 index 00000000..c2721b26 --- /dev/null +++ b/altosui/AltosBTDevice.java @@ -0,0 +1,123 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; +import java.lang.*; +import java.util.*; +import libaltosJNI.*; + +public class AltosBTDevice extends altos_bt_device implements AltosDevice { + + public String getProductName() { + String name = getName(); + if (name == null) + return "Altus Metrum"; + int dash = name.lastIndexOf("-"); + if (dash < 0) + return name; + return name.substring(0,dash); + } + + public int getProduct() { + if (Altos.bt_product_telebt.equals(getProductName())) + return Altos.product_telebt; + return 0; + } + + public String getPath() { + return getAddr(); + } + + public int getSerial() { + String name = getName(); + if (name == null) + return 0; + int dash = name.lastIndexOf("-"); + if (dash < 0 || dash >= name.length()) + return 0; + String sn = name.substring(dash + 1, name.length()); + try { + return Integer.parseInt(sn); + } catch (NumberFormatException ne) { + return 0; + } + } + + public String toString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%-20.20s %4d %s", + getProductName(), getSerial(), getAddr()); + } + + public String toShortString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%s %d %s", + getProduct(), getSerial(), getAddr()); + + } + + public SWIGTYPE_p_altos_file open() { + return libaltos.altos_bt_open(this); + } + + private boolean isAltusMetrum() { + if (getName().startsWith(Altos.bt_product_telebt)) + return true; + return false; + } + + public boolean matchProduct(int want_product) { + + System.out.printf("matchProduct %s %d\n", toString(), want_product); +// if (!isAltusMetrum()) +// return false; + + if (want_product == Altos.product_any) + return true; + + if (want_product == Altos.product_basestation) + return matchProduct(Altos.product_telebt); + + if (want_product == getProduct()) + return true; + + return false; + } + + public boolean equals(Object o) { + if (!(o instanceof AltosBTDevice)) + return false; + AltosBTDevice other = (AltosBTDevice) o; + System.out.printf("AltosBTDevice equals %s == %s\n", toString(), other.toString()); + return getName().equals(other.getName()) && getAddr().equals(other.getAddr()); + } + + public int hashCode() { + return getName().hashCode() ^ getAddr().hashCode(); + } + + public AltosBTDevice(String name, String addr) { + libaltos.altos_bt_fill_in(name, addr,this); + } + + public AltosBTDevice() { + } +} \ No newline at end of file diff --git a/altosui/AltosBTDeviceIterator.java b/altosui/AltosBTDeviceIterator.java new file mode 100644 index 00000000..7c360705 --- /dev/null +++ b/altosui/AltosBTDeviceIterator.java @@ -0,0 +1,65 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; +import java.lang.*; +import java.util.*; +import libaltosJNI.*; + +public class AltosBTDeviceIterator implements Iterator { + AltosBTDevice current; + boolean done; + SWIGTYPE_p_altos_bt_list list; + + public boolean hasNext() { + System.out.printf ("BT has next?\n"); + if (list == null) + return false; + if (current != null) + return true; + if (done) + return false; + current = new AltosBTDevice(); + while (libaltos.altos_bt_list_next(list, current) != 0) { + System.out.printf("Got BT device %s\n", current.toString()); +// if (current.matchProduct(product)) + return true; + } + current = null; + done = true; + return false; + } + + public AltosBTDevice next() { + if (hasNext()) { + AltosBTDevice next = current; + current = null; + return next; + } + return null; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public AltosBTDeviceIterator(int inquiry_time) { + done = false; + current = null; + list = libaltos.altos_bt_list_start(inquiry_time); + } +} diff --git a/altosui/AltosBTKnown.java b/altosui/AltosBTKnown.java new file mode 100644 index 00000000..95830637 --- /dev/null +++ b/altosui/AltosBTKnown.java @@ -0,0 +1,97 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; +import java.lang.*; +import java.util.*; +import libaltosJNI.*; +import java.util.prefs.*; + +public class AltosBTKnown implements Iterable { + LinkedList devices = new LinkedList(); + Preferences bt_pref = AltosPreferences.bt_devices(); + + private String get_address(String name) { + return bt_pref.get(name, ""); + } + + private void set_address(String name, String addr) { + bt_pref.put(name, addr); + System.out.printf("saving known %s %s\n", name, addr); + } + + private void remove(String name) { + bt_pref.remove(name); + } + + private void load() { + try { + String[] names = bt_pref.keys(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + String addr = get_address(name); + System.out.printf("Known device %s %s\n", name, addr); + devices.add(new AltosBTDevice(name, addr)); + } + } catch (BackingStoreException be) { + } catch (IllegalStateException ie) { + } + } + + public Iterator iterator() { + return devices.iterator(); + } + + private void flush() { + AltosPreferences.flush_preferences(); + } + + public void set(Iterable new_devices) { + for (AltosBTDevice old : devices) { + boolean found = false; + for (AltosBTDevice new_device : new_devices) { + if (new_device.equals(old)) { + found = true; + break; + } + } + if (!found) + remove(old.getName()); + } + devices = new LinkedList(); + for (AltosBTDevice new_device : new_devices) { + devices.add(new_device); + set_address(new_device.getName(), new_device.getAddr()); + } + flush(); + } + + public List list(int product) { + LinkedList list = new LinkedList(); + for (AltosBTDevice device : devices) { + if (device.matchProduct(product)) + list.add(device); + } + return list; + } + + public AltosBTKnown() { + devices = new LinkedList(); + bt_pref = AltosPreferences.bt_devices(); + load(); + } +} \ No newline at end of file diff --git a/altosui/AltosBTManage.java b/altosui/AltosBTManage.java new file mode 100644 index 00000000..98a8b757 --- /dev/null +++ b/altosui/AltosBTManage.java @@ -0,0 +1,344 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import javax.swing.table.*; +import javax.swing.event.*; +import javax.swing.plaf.basic.*; +import java.io.*; +import java.util.*; +import java.text.*; +import java.util.prefs.*; +import java.util.concurrent.*; + +import libaltosJNI.*; + +public class AltosBTManage extends JDialog implements ActionListener, Iterable { + LinkedBlockingQueue found_devices; + Frame frame; + LinkedList listeners; + AltosBTKnown bt_known; + + class DeviceList extends JList implements Iterable { + LinkedList devices; + DefaultListModel list_model; + + public void add (AltosBTDevice device) { + if (!devices.contains(device)) { + devices.add(device); + list_model.addElement(device); + } + } + + public void remove (AltosBTDevice device) { + if (devices.contains(device)) { + devices.remove(device); + list_model.removeElement(device); + } + } + + public boolean contains(AltosBTDevice device) { + return devices.contains(device); + } + + //Subclass JList to workaround bug 4832765, which can cause the + //scroll pane to not let the user easily scroll up to the beginning + //of the list. An alternative would be to set the unitIncrement + //of the JScrollBar to a fixed value. You wouldn't get the nice + //aligned scrolling, but it should work. + public int getScrollableUnitIncrement(Rectangle visibleRect, + int orientation, + int direction) { + int row; + if (orientation == SwingConstants.VERTICAL && + direction < 0 && (row = getFirstVisibleIndex()) != -1) { + Rectangle r = getCellBounds(row, row); + if ((r.y == visibleRect.y) && (row != 0)) { + Point loc = r.getLocation(); + loc.y--; + int prevIndex = locationToIndex(loc); + Rectangle prevR = getCellBounds(prevIndex, prevIndex); + + if (prevR == null || prevR.y >= r.y) { + return 0; + } + return prevR.height; + } + } + return super.getScrollableUnitIncrement( + visibleRect, orientation, direction); + } + + public Iterator iterator() { + return devices.iterator(); + } + + public java.util.List selected_list() { + java.util.LinkedList l = new java.util.LinkedList(); + Object[] a = getSelectedValues(); + for (int i = 0; i < a.length; i++) + l.add((AltosBTDevice)a[i]); + return l; + } + + public DeviceList() { + devices = new LinkedList(); + list_model = new DefaultListModel(); + setModel(list_model); + setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + setLayoutOrientation(JList.HORIZONTAL_WRAP); + setVisibleRowCount(-1); + } + } + + DeviceList visible_devices; + + DeviceList known_devices; + Thread bt_thread; + + public Iterator iterator() { + return known_devices.iterator(); + } + + public void commit() { + bt_known.set(this); + } + + public void add_known() { + for (AltosBTDevice device : visible_devices.selected_list()) { + System.out.printf("Add known %s\n", device.toString()); + known_devices.add(device); + visible_devices.remove(device); + } + } + + public void remove_known() { + for (AltosBTDevice device : known_devices.selected_list()) { + System.out.printf("Remove known %s\n", device.toString()); + known_devices.remove(device); + visible_devices.add(device); + } + } + + public void addActionListener(ActionListener l) { + listeners.add(l); + } + + private void forwardAction(ActionEvent e) { + for (ActionListener l : listeners) + l.actionPerformed(e); + } + + public void actionPerformed(ActionEvent e) { + String command = e.getActionCommand(); + System.out.printf("manage command %s\n", command); + if ("ok".equals(command)) { + bt_thread.interrupt(); + commit(); + setVisible(false); + forwardAction(e); + } else if ("cancel".equals(command)) { + bt_thread.interrupt(); + setVisible(false); + forwardAction(e); + } else if ("select".equals(command)) { + add_known(); + } else if ("deselect".equals(command)) { + remove_known(); + } + } + + public void got_visible_device() { + while (!found_devices.isEmpty()) { + AltosBTDevice device = found_devices.remove(); + if (!known_devices.contains(device)) + visible_devices.add(device); + } + } + + class BTGetVisibleDevices implements Runnable { + public void run () { + for (;;) + for (int time = 1; time <= 8; time <<= 1) { + AltosBTDeviceIterator i = new AltosBTDeviceIterator(time); + AltosBTDevice device; + + if (Thread.interrupted()) + return; + try { + while ((device = i.next()) != null) { + Runnable r; + + if (Thread.interrupted()) + return; + found_devices.add(device); + r = new Runnable() { + public void run() { + got_visible_device(); + } + }; + SwingUtilities.invokeLater(r); + } + } catch (Exception e) { + System.out.printf("uh-oh, exception %s\n", e.toString()); + } + } + } + } + + public static void show(Component frameComp, AltosBTKnown known) { + Frame frame = JOptionPane.getFrameForComponent(frameComp); + AltosBTManage dialog; + + dialog = new AltosBTManage(frame, known); + dialog.setVisible(true); + } + + public AltosBTManage(Frame in_frame, AltosBTKnown in_known) { + super(in_frame, "Manage Bluetooth Devices", true); + + frame = in_frame; + bt_known = in_known; + BTGetVisibleDevices get_visible_devices = new BTGetVisibleDevices(); + bt_thread = new Thread(get_visible_devices); + bt_thread.start(); + + listeners = new LinkedList(); + + found_devices = new LinkedBlockingQueue(); + + Container pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.insets = new Insets(4,4,4,4); + + /* + * Known devices label and list + */ + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + pane.add(new JLabel("Known Devices"), c); + + known_devices = new DeviceList(); + for (AltosBTDevice device : bt_known) + known_devices.add(device); + + JScrollPane known_list_scroller = new JScrollPane(known_devices); + known_list_scroller.setPreferredSize(new Dimension(400, 80)); + known_list_scroller.setAlignmentX(LEFT_ALIGNMENT); + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 2; + pane.add(known_list_scroller, c); + + /* + * Visible devices label and list + */ + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + c.gridx = 2; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + pane.add(new JLabel("Visible Devices"), c); + + visible_devices = new DeviceList(); + JScrollPane visible_list_scroller = new JScrollPane(visible_devices); + visible_list_scroller.setPreferredSize(new Dimension(400, 80)); + visible_list_scroller.setAlignmentX(LEFT_ALIGNMENT); + c.fill = GridBagConstraints.BOTH; + c.anchor = GridBagConstraints.WEST; + c.gridx = 2; + c.gridy = 1; + c.gridheight = 2; + c.gridwidth = 1; + pane.add(visible_list_scroller, c); + + /* + * Arrows between the two lists + */ + BasicArrowButton select_arrow = new BasicArrowButton(SwingConstants.WEST); + select_arrow.setActionCommand("select"); + select_arrow.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.SOUTH; + c.gridx = 1; + c.gridy = 1; + c.gridheight = 1; + c.gridwidth = 1; + pane.add(select_arrow, c); + + BasicArrowButton deselect_arrow = new BasicArrowButton(SwingConstants.EAST); + deselect_arrow.setActionCommand("deselect"); + deselect_arrow.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.NORTH; + c.gridx = 1; + c.gridy = 2; + c.gridheight = 1; + c.gridwidth = 1; + pane.add(deselect_arrow, c); + + JButton cancel_button = new JButton("Cancel"); + cancel_button.setActionCommand("cancel"); + cancel_button.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 0; + c.gridy = 3; + c.gridheight = 1; + c.gridwidth = 1; + pane.add(cancel_button, c); + + JButton ok_button = new JButton("OK"); + ok_button.setActionCommand("ok"); + ok_button.addActionListener(this); + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.CENTER; + c.gridx = 2; + c.gridy = 3; + c.gridheight = 1; + c.gridwidth = 1; + pane.add(ok_button, c); + + getRootPane().setDefaultButton(ok_button); + + pack(); + setLocationRelativeTo(frame); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + bt_thread.interrupt(); + setVisible(false); + } + }); + } +} diff --git a/altosui/AltosConfig.java b/altosui/AltosConfig.java index f45e2040..c5de83f2 100644 --- a/altosui/AltosConfig.java +++ b/altosui/AltosConfig.java @@ -344,11 +344,11 @@ public class AltosConfig implements ActionListener { version = new string_ref("unknown"); product = new string_ref("unknown"); - device = AltosDeviceDialog.show(owner, AltosDevice.product_any); + device = AltosDeviceDialog.show(owner, Altos.product_any); if (device != null) { try { serial_line = new AltosSerial(device); - if (!device.matchProduct(AltosDevice.product_telemetrum)) + if (!device.matchProduct(Altos.product_telemetrum)) remote = true; try { init_ui(); diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java index 9a292c91..0f5e4a3b 100644 --- a/altosui/AltosConfigureUI.java +++ b/altosui/AltosConfigureUI.java @@ -49,6 +49,8 @@ public class AltosConfigureUI JRadioButton serial_debug; + JButton manage_bluetooth; + /* DocumentListener interface methods */ public void changedUpdate(DocumentEvent e) { AltosPreferences.set_callsign(callsign_value.getText()); @@ -199,6 +201,19 @@ public class AltosConfigureUI c.anchor = GridBagConstraints.WEST; pane.add(serial_debug, c); + manage_bluetooth = new JButton("Manage Bluetooth"); + manage_bluetooth.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + AltosBTManage.show(owner, Altos.bt_known); + } + }); + c.gridx = 1; + c.gridy = 6; + c.gridwidth = 3; + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.WEST; + pane.add(manage_bluetooth, c); + /* And a close button at the bottom */ close = new JButton("Close"); close.addActionListener(new ActionListener() { @@ -207,7 +222,7 @@ public class AltosConfigureUI } }); c.gridx = 0; - c.gridy = 6; + c.gridy = 7; c.gridwidth = 3; c.fill = GridBagConstraints.NONE; c.anchor = GridBagConstraints.CENTER; diff --git a/altosui/AltosDevice.java b/altosui/AltosDevice.java index f0fda57b..3357c550 100644 --- a/altosui/AltosDevice.java +++ b/altosui/AltosDevice.java @@ -1,5 +1,5 @@ /* - * Copyright © 2010 Keith Packard + * Copyright © 2011 Keith Packard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,151 +20,11 @@ import java.lang.*; import java.util.*; import libaltosJNI.*; -public class AltosDevice extends altos_device { - - static public boolean initialized = false; - static public boolean loaded_library = false; - - public static boolean load_library() { - if (!initialized) { - try { - System.loadLibrary("altos"); - libaltos.altos_init(); - loaded_library = true; - } catch (UnsatisfiedLinkError e) { - loaded_library = false; - } - initialized = true; - } - return loaded_library; - } - - static int usb_vendor_altusmetrum() { - if (load_library()) - return libaltosConstants.USB_VENDOR_ALTUSMETRUM; - return 0x000a; - } - - static int usb_product_altusmetrum() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM; - return 0x000a; - } - - static int usb_product_altusmetrum_min() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MIN; - return 0x000a; - } - - static int usb_product_altusmetrum_max() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_ALTUSMETRUM_MAX; - return 0x000d; - } - - static int usb_product_telemetrum() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELEMETRUM; - return 0x000b; - } - - static int usb_product_teledongle() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELEDONGLE; - return 0x000c; - } - - static int usb_product_teleterra() { - if (load_library()) - return libaltosConstants.USB_PRODUCT_TELETERRA; - return 0x000d; - } - - public final static int vendor_altusmetrum = usb_vendor_altusmetrum(); - public final static int product_altusmetrum = usb_product_altusmetrum(); - public final static int product_telemetrum = usb_product_telemetrum(); - public final static int product_teledongle = usb_product_teledongle(); - public final static int product_teleterra = usb_product_teleterra(); - public final static int product_altusmetrum_min = usb_product_altusmetrum_min(); - public final static int product_altusmetrum_max = usb_product_altusmetrum_max(); - - - public final static int product_any = 0x10000; - public final static int product_basestation = 0x10000 + 1; - - public String toString() { - String name = getName(); - if (name == null) - name = "Altus Metrum"; - return String.format("%-20.20s %4d %s", - getName(), getSerial(), getPath()); - } - - public String toShortString() { - String name = getName(); - if (name == null) - name = "Altus Metrum"; - return String.format("%s %d %s", - name, getSerial(), getPath()); - - } - - public boolean isAltusMetrum() { - if (getVendor() != vendor_altusmetrum) - return false; - if (getProduct() < product_altusmetrum_min) - return false; - if (getProduct() > product_altusmetrum_max) - return false; - return true; - } - - public boolean matchProduct(int want_product) { - - if (!isAltusMetrum()) - return false; - - if (want_product == product_any) - return true; - - if (want_product == product_basestation) - return matchProduct(product_teledongle) || matchProduct(product_teleterra); - - int have_product = getProduct(); - - if (have_product == product_altusmetrum) /* old devices match any request */ - return true; - - if (want_product == have_product) - return true; - - return false; - } - - static AltosDevice[] list(int product) { - if (!load_library()) - return null; - - SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); - - ArrayList device_list = new ArrayList(); - if (list != null) { - SWIGTYPE_p_altos_file file; - - for (;;) { - AltosDevice device = new AltosDevice(); - if (libaltos.altos_list_next(list, device) == 0) - break; - if (device.matchProduct(product)) - device_list.add(device); - } - libaltos.altos_list_finish(list); - } - - AltosDevice[] devices = new AltosDevice[device_list.size()]; - for (int i = 0; i < device_list.size(); i++) - devices[i] = device_list.get(i); - return devices; - } -} \ No newline at end of file +public interface AltosDevice { + public abstract String toString(); + public abstract String toShortString(); + public abstract int getSerial(); + public abstract String getPath(); + public abstract boolean matchProduct(int product); + public SWIGTYPE_p_altos_file open(); +} diff --git a/altosui/AltosDeviceDialog.java b/altosui/AltosDeviceDialog.java index 2966ad1e..e17504e2 100644 --- a/altosui/AltosDeviceDialog.java +++ b/altosui/AltosDeviceDialog.java @@ -22,59 +22,64 @@ import java.util.*; import javax.swing.*; import java.awt.*; import java.awt.event.*; -import libaltosJNI.libaltos; -import libaltosJNI.altos_device; -import libaltosJNI.SWIGTYPE_p_altos_file; -import libaltosJNI.SWIGTYPE_p_altos_list; +import libaltosJNI.*; public class AltosDeviceDialog extends JDialog implements ActionListener { - private static AltosDeviceDialog dialog; - private static AltosDevice value = null; - private JList list; + private AltosDevice value; + private JList list; + private JButton cancel_button; + private JButton select_button; + private JButton manage_bluetooth_button; + private Frame frame; + private int product; - public static AltosDevice show (Component frameComp, int product) { + private AltosDevice getValue() { + return value; + } - Frame frame = JOptionPane.getFrameForComponent(frameComp); - AltosDevice[] devices; - devices = AltosDevice.list(product); - - if (devices != null && devices.length > 0) { - value = null; - dialog = new AltosDeviceDialog(frame, frameComp, - devices, - devices[0]); - - dialog.setVisible(true); - return value; - } else { - /* check for missing altos JNI library, which - * will put up its own error dialog - */ - if (AltosUI.load_library(frame)) { - JOptionPane.showMessageDialog(frame, - "No AltOS devices available", - "No AltOS devices", - JOptionPane.ERROR_MESSAGE); - } - return null; - } + private AltosDevice[] devices() { + java.util.List usb_devices = AltosUSBDevice.list(product); + java.util.List bt_devices = Altos.bt_known.list(product); + AltosDevice[] devices = new AltosDevice[usb_devices.size() + bt_devices.size()]; + + for (int i = 0; i < usb_devices.size(); i++) + devices[i] = usb_devices.get(i); + int off = usb_devices.size(); + for (int j = 0; j < bt_devices.size(); j++) + devices[off + j] = bt_devices.get(j); + return devices; } - private AltosDeviceDialog (Frame frame, Component location, - AltosDevice[] devices, - AltosDevice initial) { - super(frame, "Device Selection", true); + private void update_devices() { + AltosDevice[] devices = devices(); + list.setListData(devices); + select_button.setEnabled(devices.length > 0); + } + private AltosDeviceDialog (Frame in_frame, Component location, int in_product) { + super(in_frame, "Device Selection", true); + + product = in_product; + frame = in_frame; value = null; - JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener(this); + AltosDevice[] devices = devices(); + + cancel_button = new JButton("Cancel"); + cancel_button.setActionCommand("cancel"); + cancel_button.addActionListener(this); + + manage_bluetooth_button = new JButton("Manage Bluetooth"); + manage_bluetooth_button.setActionCommand("manage"); + manage_bluetooth_button.addActionListener(this); - final JButton selectButton = new JButton("Select"); - selectButton.setActionCommand("select"); - selectButton.addActionListener(this); - getRootPane().setDefaultButton(selectButton); + select_button = new JButton("Select"); + select_button.setActionCommand("select"); + select_button.addActionListener(this); + if (devices.length == 0) + select_button.setEnabled(false); + getRootPane().setDefaultButton(select_button); list = new JList(devices) { //Subclass JList to workaround bug 4832765, which can cause the @@ -112,7 +117,7 @@ public class AltosDeviceDialog extends JDialog implements ActionListener { list.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { - selectButton.doClick(); //emulate button click + select_button.doClick(); //emulate button click } } }); @@ -139,9 +144,11 @@ public class AltosDeviceDialog extends JDialog implements ActionListener { buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); buttonPane.add(Box.createHorizontalGlue()); - buttonPane.add(cancelButton); + buttonPane.add(cancel_button); buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); - buttonPane.add(selectButton); + buttonPane.add(manage_bluetooth_button); + buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); + buttonPane.add(select_button); //Put everything together, using the content pane's BorderLayout. Container contentPane = getContentPane(); @@ -149,7 +156,8 @@ public class AltosDeviceDialog extends JDialog implements ActionListener { contentPane.add(buttonPane, BorderLayout.PAGE_END); //Initialize values. - list.setSelectedValue(initial, true); + if (devices != null && devices.length != 0) + list.setSelectedValue(devices[0], true); pack(); setLocationRelativeTo(location); } @@ -157,8 +165,22 @@ public class AltosDeviceDialog extends JDialog implements ActionListener { //Handle clicks on the Set and Cancel buttons. public void actionPerformed(ActionEvent e) { if ("select".equals(e.getActionCommand())) - AltosDeviceDialog.value = (AltosDevice)(list.getSelectedValue()); - AltosDeviceDialog.dialog.setVisible(false); + value = (AltosDevice)(list.getSelectedValue()); + if ("manage".equals(e.getActionCommand())) { + AltosBTManage.show(frame, Altos.bt_known); + update_devices(); + return; + } + setVisible(false); } + public static AltosDevice show (Component frameComp, int product) { + + Frame frame = JOptionPane.getFrameForComponent(frameComp); + AltosDeviceDialog dialog; + + dialog = new AltosDeviceDialog(frame, frameComp, product); + dialog.setVisible(true); + return dialog.getValue(); + } } diff --git a/altosui/AltosEepromDownload.java b/altosui/AltosEepromDownload.java index 3dd5b12f..82f01ef5 100644 --- a/altosui/AltosEepromDownload.java +++ b/altosui/AltosEepromDownload.java @@ -159,6 +159,7 @@ public class AltosEepromDownload implements Runnable { r = new AltosEepromRecord(Altos.AO_LOG_STATE, tiny_tick, state, 0); if (state == Altos.ao_flight_landed) done = true; + state = s; any_valid = true; } else { if (v != 0xffff) diff --git a/altosui/AltosEepromManage.java b/altosui/AltosEepromManage.java index b46364db..cd2b74fe 100644 --- a/altosui/AltosEepromManage.java +++ b/altosui/AltosEepromManage.java @@ -197,7 +197,7 @@ public class AltosEepromManage implements ActionListener { boolean running = false; frame = given_frame; - device = AltosDeviceDialog.show(frame, AltosDevice.product_any); + device = AltosDeviceDialog.show(frame, Altos.product_any); remote = false; any_download = false; @@ -206,7 +206,7 @@ public class AltosEepromManage implements ActionListener { if (device != null) { try { serial_line = new AltosSerial(device); - if (!device.matchProduct(AltosDevice.product_telemetrum)) + if (!device.matchProduct(Altos.product_telemetrum)) remote = true; serial_line.set_frame(frame); diff --git a/altosui/AltosFlashUI.java b/altosui/AltosFlashUI.java index 0302ccd3..ad7aeac8 100644 --- a/altosui/AltosFlashUI.java +++ b/altosui/AltosFlashUI.java @@ -151,7 +151,7 @@ public class AltosFlashUI build_dialog(); - debug_dongle = AltosDeviceDialog.show(frame, AltosDevice.product_any); + debug_dongle = AltosDeviceDialog.show(frame, Altos.product_any); if (debug_dongle == null) return; diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java index 66dcdad5..eb6c6d9d 100644 --- a/altosui/AltosFlightUI.java +++ b/altosui/AltosFlightUI.java @@ -122,7 +122,7 @@ public class AltosFlightUI extends JFrame implements AltosFlightDisplay { JComboBox telemetries; public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) { - AltosPreferences.init(this); + AltosPreferences.set_component(this); voice = in_voice; reader = in_reader; diff --git a/altosui/AltosIgnite.java b/altosui/AltosIgnite.java index 1171d2ed..7a06c63d 100644 --- a/altosui/AltosIgnite.java +++ b/altosui/AltosIgnite.java @@ -172,7 +172,7 @@ public class AltosIgnite { serial = new AltosSerial(device); remote = false; - if (!device.matchProduct(AltosDevice.product_telemetrum)) + if (!device.matchProduct(Altos.product_telemetrum)) remote = true; } } \ No newline at end of file diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java index 000adc98..ad5b7cfb 100644 --- a/altosui/AltosIgniteUI.java +++ b/altosui/AltosIgniteUI.java @@ -275,7 +275,7 @@ public class AltosIgniteUI private boolean open() { command_queue = new LinkedBlockingQueue(); - device = AltosDeviceDialog.show(owner, AltosDevice.product_any); + device = AltosDeviceDialog.show(owner, Altos.product_any); if (device != null) { try { AltosIgnite ignite = new AltosIgnite(device); diff --git a/altosui/AltosPreferences.java b/altosui/AltosPreferences.java index 5f827655..b1192be1 100644 --- a/altosui/AltosPreferences.java +++ b/altosui/AltosPreferences.java @@ -79,11 +79,9 @@ class AltosPreferences { /* Serial debug */ static boolean serial_debug; - public static void init(Component ui) { + public static void init() { preferences = Preferences.userRoot().node("/org/altusmetrum/altosui"); - component = ui; - /* Initialize logdir from preferences */ String logdir_string = preferences.get(logdirPreference, null); if (logdir_string != null) @@ -116,14 +114,23 @@ class AltosPreferences { AltosSerial.set_debug(serial_debug); } + static { init(); } + + static void set_component(Component in_component) { + component = in_component; + } + static void flush_preferences() { try { preferences.flush(); } catch (BackingStoreException ee) { - JOptionPane.showMessageDialog(component, - preferences.absolutePath(), - "Cannot save prefernces", - JOptionPane.ERROR_MESSAGE); + if (component != null) + JOptionPane.showMessageDialog(component, + preferences.absolutePath(), + "Cannot save prefernces", + JOptionPane.ERROR_MESSAGE); + else + System.err.printf("Cannot save preferences\n"); } } @@ -262,4 +269,8 @@ class AltosPreferences { public static boolean serial_debug() { return serial_debug; } + + public static Preferences bt_devices() { + return preferences.node("bt_devices"); + } } diff --git a/altosui/AltosSerial.java b/altosui/AltosSerial.java index 111bd771..6c80b66f 100644 --- a/altosui/AltosSerial.java +++ b/altosui/AltosSerial.java @@ -304,7 +304,7 @@ public class AltosSerial implements Runnable { throw new AltosSerialInUseException(device); devices_opened.add(device.getPath()); } - altos = libaltos.altos_open(device); + altos = device.open(); if (altos == null) { close(); throw new FileNotFoundException(device.toShortString()); diff --git a/altosui/AltosSerialInUseException.java b/altosui/AltosSerialInUseException.java index 4b108c7c..7380f331 100644 --- a/altosui/AltosSerialInUseException.java +++ b/altosui/AltosSerialInUseException.java @@ -17,12 +17,10 @@ package altosui; -import libaltosJNI.*; - public class AltosSerialInUseException extends Exception { - public altos_device device; + public AltosDevice device; - public AltosSerialInUseException (altos_device in_device) { + public AltosSerialInUseException (AltosDevice in_device) { device = in_device; } } diff --git a/altosui/AltosSiteMap.java b/altosui/AltosSiteMap.java index f4b6b7e0..7575c10e 100644 --- a/altosui/AltosSiteMap.java +++ b/altosui/AltosSiteMap.java @@ -164,8 +164,6 @@ public class AltosSiteMap extends JScrollPane implements AltosFlightDisplay { } public static void prefetchMaps(double lat, double lng, int w, int h) { - AltosPreferences.init(null); - AltosSiteMap asm = new AltosSiteMap(true); asm.centre = asm.getBaseLocation(lat, lng); diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java index 4d17b0d2..7955c1c2 100644 --- a/altosui/AltosUI.java +++ b/altosui/AltosUI.java @@ -34,7 +34,7 @@ public class AltosUI extends JFrame { public AltosVoice voice = new AltosVoice(); public static boolean load_library(Frame frame) { - if (!AltosDevice.load_library()) { + if (!Altos.load_library()) { JOptionPane.showMessageDialog(frame, String.format("No AltOS library in \"%s\"", System.getProperty("java.library.path","")), @@ -99,7 +99,7 @@ public class AltosUI extends JFrame { if (imgURL != null) setIconImage(new ImageIcon(imgURL).getImage()); - AltosPreferences.init(this); + AltosPreferences.set_component(this); pane = getContentPane(); gridbag = new GridBagLayout(); @@ -199,7 +199,7 @@ public class AltosUI extends JFrame { private void ConnectToDevice() { AltosDevice device = AltosDeviceDialog.show(AltosUI.this, - AltosDevice.product_basestation); + Altos.product_basestation); if (device != null) telemetry_window(device); @@ -397,9 +397,9 @@ public class AltosUI extends JFrame { AltosUI altosui = new AltosUI(); altosui.setVisible(true); - AltosDevice[] devices = AltosDevice.list(AltosDevice.product_basestation); - for (int i = 0; i < devices.length; i++) - altosui.telemetry_window(devices[i]); + java.util.List devices = AltosUSBDevice.list(Altos.product_basestation); + for (AltosDevice device : devices) + altosui.telemetry_window(device); } } } diff --git a/altosui/AltosUSBDevice.java b/altosui/AltosUSBDevice.java new file mode 100644 index 00000000..dc746a64 --- /dev/null +++ b/altosui/AltosUSBDevice.java @@ -0,0 +1,100 @@ +/* + * Copyright © 2010 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +package altosui; +import java.lang.*; +import java.util.*; +import libaltosJNI.*; + +public class AltosUSBDevice extends altos_device implements AltosDevice { + + public String toString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%-20.20s %4d %s", + name, getSerial(), getPath()); + } + + public String toShortString() { + String name = getName(); + if (name == null) + name = "Altus Metrum"; + return String.format("%s %d %s", + name, getSerial(), getPath()); + + } + + public SWIGTYPE_p_altos_file open() { + return libaltos.altos_open(this); + } + + private boolean isAltusMetrum() { + if (getVendor() != Altos.vendor_altusmetrum) + return false; + if (getProduct() < Altos.product_altusmetrum_min) + return false; + if (getProduct() > Altos.product_altusmetrum_max) + return false; + return true; + } + + public boolean matchProduct(int want_product) { + + if (!isAltusMetrum()) + return false; + + if (want_product == Altos.product_any) + return true; + + if (want_product == Altos.product_basestation) + return matchProduct(Altos.product_teledongle) || + matchProduct(Altos.product_teleterra) || + matchProduct(Altos.product_telebt); + + int have_product = getProduct(); + + if (have_product == Altos.product_altusmetrum) /* old devices match any request */ + return true; + + if (want_product == have_product) + return true; + + return false; + } + + static java.util.List list(int product) { + if (!Altos.load_library()) + return null; + + SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); + + ArrayList device_list = new ArrayList(); + if (list != null) { + for (;;) { + AltosUSBDevice device = new AltosUSBDevice(); + if (libaltos.altos_list_next(list, device) == 0) + break; + if (device.matchProduct(product)) + device_list.add(device); + } + libaltos.altos_list_finish(list); + } + + return device_list; + } +} \ No newline at end of file diff --git a/altosui/Makefile.am b/altosui/Makefile.am index 01fe50c8..f4d84ad2 100644 --- a/altosui/Makefile.am +++ b/altosui/Makefile.am @@ -26,6 +26,11 @@ altosui_JAVA = \ AltosDescent.java \ AltosDeviceDialog.java \ AltosDevice.java \ + AltosUSBDevice.java \ + AltosBTDevice.java \ + AltosBTDeviceIterator.java \ + AltosBTManage.java \ + AltosBTKnown.java \ AltosDisplayThread.java \ AltosEepromChunk.java \ AltosEepromDelete.java \ diff --git a/altosui/libaltos/Makefile.am b/altosui/libaltos/Makefile.am index 388d2104..3f5f3ee2 100644 --- a/altosui/libaltos/Makefile.am +++ b/altosui/libaltos/Makefile.am @@ -16,7 +16,7 @@ noinst_PROGRAMS=cjnitest cjnitest_LDADD=libaltos.la -LIBS= +LIBS=-lbluetooth HFILES=libaltos.h diff --git a/altosui/libaltos/cjnitest.c b/altosui/libaltos/cjnitest.c index c6d6e069..88e40d73 100644 --- a/altosui/libaltos/cjnitest.c +++ b/altosui/libaltos/cjnitest.c @@ -14,6 +14,8 @@ main () { struct altos_device device; struct altos_list *list; + struct altos_bt_device bt_device; + struct altos_bt_list *bt_list; altos_init(); list = altos_list_start(); @@ -39,5 +41,29 @@ main () altos_close(file); } altos_list_finish(list); + bt_list = altos_bt_list_start(8); + while (altos_bt_list_next(bt_list, &bt_device)) { + printf ("%s %s\n", bt_device.name, bt_device.addr); + if (strncmp(bt_device.name, "TeleBT", 6) == 0) { + struct altos_file *file; + + int c; + file = altos_bt_open(&bt_device); + if (!file) { + printf("altos_bt_open failed\n"); + continue; + } + altos_puts(file,"v\nc s\n"); + altos_flush(file); + while ((c = altos_getchar(file, 100)) >= 0) { + putchar(c); + } + if (c != LIBALTOS_TIMEOUT) + printf("getchar returns %d\n", c); + altos_close(file); + } + } + altos_bt_list_finish(bt_list); altos_fini(); + return 0; } diff --git a/altosui/libaltos/libaltos.c b/altosui/libaltos/libaltos.c index 465f0ac8..c5bcf900 100644 --- a/altosui/libaltos/libaltos.c +++ b/altosui/libaltos/libaltos.c @@ -56,6 +56,230 @@ altos_strndup (const char *s, size_t n) #define altos_strndup strndup #endif +#ifdef POSIX_TTY + +#include +#include +#include +#include +#include + +#define USB_BUF_SIZE 64 + +struct altos_file { + int fd; +#ifdef USE_POLL + int pipe[2]; +#else + int out_fd; +#endif + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (sizeof (struct altos_file), 1); + int ret; + struct termios term; + + if (!file) + return NULL; + + file->fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->fd < 0) { + perror(device->path); + free(file); + return NULL; + } +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->out_fd < 0) { + perror(device->path); + close(file->fd); + free(file); + return NULL; + } +#endif + ret = tcgetattr(file->fd, &term); + if (ret < 0) { + perror("tcgetattr"); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + cfmakeraw(&term); +#ifdef USE_POLL + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; +#else + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; +#endif + ret = tcsetattr(file->fd, TCSAFLUSH, &term); + if (ret < 0) { + perror("tcsetattr"); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + return file; +} + +PUBLIC void +altos_close(struct altos_file *file) +{ + if (file->fd != -1) { + int fd = file->fd; + file->fd = -1; +#ifdef USE_POLL + write(file->pipe[1], "\r", 1); +#else + close(file->out_fd); + file->out_fd = -1; +#endif + close(fd); + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} + +PUBLIC int +altos_flush(struct altos_file *file) +{ + if (file->out_used && 0) { + printf ("flush \""); + fwrite(file->out_data, 1, file->out_used, stdout); + printf ("\"\n"); + } + while (file->out_used) { + int ret; + + if (file->fd < 0) + return -EBADF; +#ifdef USE_POLL + ret = write (file->fd, file->out_data, file->out_used); +#else + ret = write (file->out_fd, file->out_data, file->out_used); +#endif + if (ret < 0) + return -errno; + if (ret) { + memmove(file->out_data, file->out_data + ret, + file->out_used - ret); + file->out_used -= ret; + } + } + return 0; +} + +PUBLIC int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) { + return ret; + } + } + file->out_data[file->out_used++] = c; + ret = 0; + if (file->out_used == USB_BUF_SIZE) + ret = altos_flush(file); + return 0; +} + +#ifdef USE_POLL +#include +#endif + +static int +altos_fill(struct altos_file *file, int timeout) +{ + int ret; +#ifdef USE_POLL + struct pollfd fd[2]; +#endif + + if (timeout == 0) + timeout = -1; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; +#ifdef USE_POLL + fd[0].fd = file->fd; + fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; + fd[1].fd = file->pipe[0]; + fd[1].events = POLLIN; + ret = poll(fd, 2, timeout); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + if (ret == 0) + return LIBALTOS_TIMEOUT; + + if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) + return LIBALTOS_ERROR; + if (fd[0].revents & POLLIN) +#endif + { + ret = read(file->fd, file->in_data, USB_BUF_SIZE); + if (ret < 0) { + perror("altos_getchar"); + return LIBALTOS_ERROR; + } + file->in_read = 0; + file->in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif + } + } + if (file->in_used && 0) { + printf ("fill \""); + fwrite(file->in_data, 1, file->in_used, stdout); + printf ("\"\n"); + } + return 0; +} + +PUBLIC int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +#endif /* POSIX_TTY */ + /* * Scan for Altus Metrum devices by looking through /sys */ @@ -68,6 +292,10 @@ altos_strndup (const char *s, size_t n) #include #include #include +#include +#include +#include +#include static char * cc_fullname (char *dir, char *file) @@ -354,6 +582,138 @@ altos_list_finish(struct altos_list *usbdevs) free(usbdevs); } +struct altos_bt_list { + inquiry_info *ii; + int sock; + int dev_id; + int rsp; + int num_rsp; +}; + +#define INQUIRY_MAX_RSP 255 + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + struct altos_bt_list *bt_list; + + bt_list = calloc(1, sizeof (struct altos_bt_list)); + if (!bt_list) + goto no_bt_list; + + bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); + if (!bt_list->ii) + goto no_ii; + bt_list->dev_id = hci_get_route(NULL); + if (bt_list->dev_id < 0) + goto no_dev_id; + + bt_list->sock = hci_open_dev(bt_list->dev_id); + if (bt_list->sock < 0) + goto no_sock; + + bt_list->num_rsp = hci_inquiry(bt_list->dev_id, + inquiry_time, + INQUIRY_MAX_RSP, + NULL, + &bt_list->ii, + IREQ_CACHE_FLUSH); + if (bt_list->num_rsp < 0) + goto no_rsp; + + bt_list->rsp = 0; + return bt_list; + +no_rsp: + close(bt_list->sock); +no_sock: +no_dev_id: + free(bt_list->ii); +no_ii: + free(bt_list); +no_bt_list: + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + inquiry_info *ii; + + if (bt_list->rsp >= bt_list->num_rsp) + return 0; + + ii = &bt_list->ii[bt_list->rsp]; + ba2str(&ii->bdaddr, device->addr); + memset(&device->name, '\0', sizeof (device->name)); + if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, + sizeof (device->name), + device->name, 0) < 0) { + strcpy(device->name, "[unknown]"); + } + bt_list->rsp++; + return 1; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ + close(bt_list->sock); + free(bt_list->ii); + free(bt_list); +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + struct sockaddr_rc addr = { 0 }; + int s, status; + struct altos_file *file; + + file = calloc(1, sizeof (struct altos_file)); + if (!file) + goto no_file; + file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (file->fd < 0) + goto no_sock; + + addr.rc_family = AF_BLUETOOTH; + addr.rc_channel = 1; + str2ba(device->addr, &addr.rc_bdaddr); + + status = connect(file->fd, + (struct sockaddr *)&addr, + sizeof(addr)); + if (status < 0) { + perror("connect"); + goto no_link; + } + sleep(2); + +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = dup(file->fd); +#endif + return file; +no_link: + close(s); +no_sock: + free(file); +no_file: + return NULL; +} + #endif #ifdef DARWIN @@ -417,7 +777,7 @@ get_number(io_object_t object, CFStringRef entry, int *result) } struct altos_list * -altos_list_start(void) +altos_list_start(int time) { struct altos_list *list = calloc (sizeof (struct altos_list), 1); CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); @@ -468,229 +828,6 @@ altos_list_finish(struct altos_list *list) #endif -#ifdef POSIX_TTY - -#include -#include -#include -#include -#include - -#define USB_BUF_SIZE 64 - -struct altos_file { - int fd; -#ifdef USE_POLL - int pipe[2]; -#else - int out_fd; -#endif - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; -}; - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (sizeof (struct altos_file), 1); - int ret; - struct termios term; - - if (!file) - return NULL; - - file->fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->fd < 0) { - perror(device->path); - free(file); - return NULL; - } -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->out_fd < 0) { - perror(device->path); - close(file->fd); - free(file); - return NULL; - } -#endif - ret = tcgetattr(file->fd, &term); - if (ret < 0) { - perror("tcgetattr"); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - cfmakeraw(&term); -#ifdef USE_POLL - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 0; -#else - term.c_cc[VMIN] = 0; - term.c_cc[VTIME] = 1; -#endif - ret = tcsetattr(file->fd, TCSAFLUSH, &term); - if (ret < 0) { - perror("tcsetattr"); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->fd != -1) { - int fd = file->fd; - file->fd = -1; -#ifdef USE_POLL - write(file->pipe[1], "\r", 1); -#else - close(file->out_fd); - file->out_fd = -1; -#endif - close(fd); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - if (file->out_used && 0) { - printf ("flush \""); - fwrite(file->out_data, 1, file->out_used, stdout); - printf ("\"\n"); - } - while (file->out_used) { - int ret; - - if (file->fd < 0) - return -EBADF; -#ifdef USE_POLL - ret = write (file->fd, file->out_data, file->out_used); -#else - ret = write (file->out_fd, file->out_data, file->out_used); -#endif - if (ret < 0) - return -errno; - if (ret) { - memmove(file->out_data, file->out_data + ret, - file->out_used - ret); - file->out_used -= ret; - } - } - return 0; -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) { - return ret; - } - } - file->out_data[file->out_used++] = c; - ret = 0; - if (file->out_used == USB_BUF_SIZE) - ret = altos_flush(file); - return 0; -} - -#ifdef USE_POLL -#include -#endif - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; -#ifdef USE_POLL - struct pollfd fd[2]; -#endif - - if (timeout == 0) - timeout = -1; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; -#ifdef USE_POLL - fd[0].fd = file->fd; - fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - fd[1].fd = file->pipe[0]; - fd[1].events = POLLIN; - ret = poll(fd, 2, timeout); - if (ret < 0) { - perror("altos_getchar"); - return LIBALTOS_ERROR; - } - if (ret == 0) - return LIBALTOS_TIMEOUT; - - if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) - return LIBALTOS_ERROR; - if (fd[0].revents & POLLIN) -#endif - { - ret = read(file->fd, file->in_data, USB_BUF_SIZE); - if (ret < 0) { - perror("altos_getchar"); - return LIBALTOS_ERROR; - } - file->in_read = 0; - file->in_used = ret; -#ifndef USE_POLL - if (ret == 0 && timeout > 0) - return LIBALTOS_TIMEOUT; -#endif - } - } - if (file->in_used && 0) { - printf ("fill \""); - fwrite(file->in_data, 1, file->in_used, stdout); - printf ("\"\n"); - } - return 0; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -#endif /* POSIX_TTY */ #ifdef WINDOWS diff --git a/altosui/libaltos/libaltos.h b/altosui/libaltos/libaltos.h index 6e94899e..f710919c 100644 --- a/altosui/libaltos/libaltos.h +++ b/altosui/libaltos/libaltos.h @@ -40,6 +40,7 @@ #define USB_PRODUCT_TELEMETRUM 0x000b #define USB_PRODUCT_TELEDONGLE 0x000c #define USB_PRODUCT_TELETERRA 0x000d +#define USB_PRODUCT_TELEBT 0x000e #define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a #define USB_PRODUCT_ALTUSMETRUM_MAX 0x0013 @@ -57,6 +58,15 @@ struct altos_device { //%mutable; }; +#define BLUETOOTH_PRODUCT_TELEBT "TeleBT" + +struct altos_bt_device { + //%immutable; + char name[256]; + char addr[20]; + //%mutable; +}; + #define LIBALTOS_SUCCESS 0 #define LIBALTOS_ERROR -1 #define LIBALTOS_TIMEOUT -2 @@ -99,4 +109,19 @@ altos_flush(struct altos_file *file); PUBLIC int altos_getchar(struct altos_file *file, int timeout); +PUBLIC struct altos_bt_list * +altos_bt_list_start(int inquiry_time); + +PUBLIC int +altos_bt_list_next(struct altos_bt_list *list, struct altos_bt_device *device); + +PUBLIC void +altos_bt_list_finish(struct altos_bt_list *list); + +PUBLIC void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device); + +PUBLIC struct altos_file * +altos_bt_open(struct altos_bt_device *device); + #endif /* _LIBALTOS_H_ */ diff --git a/src/Makefile b/src/Makefile index a5dec57b..d83ec668 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,6 +10,7 @@ SUBDIRS=\ telemetrum-v1.1 telemetrum-v1.0 \ teledongle-v0.2 teledongle-v0.1 \ telemini-v0.1 telenano-v0.1 \ + telebt-v0.0 \ telemetrum-v0.1-sky telemetrum-v0.1-sirf \ tidongle test diff --git a/src/Makefile.proto b/src/Makefile.proto index 5aad445f..ca68edbc 100644 --- a/src/Makefile.proto +++ b/src/Makefile.proto @@ -136,12 +136,20 @@ M25_DRIVER_SRC = \ # SIRF_DRIVER_SRC = \ ao_gps_sirf.c + # # Skytraq driver source # SKY_DRIVER_SRC = \ ao_gps_skytraq.c + +# +# BTM-182 driver source +# +BTM_DRIVER_SRC = \ + ao_btm.c + # # Tasks run on TeleMetrum # @@ -229,6 +237,24 @@ TNANO_BASE_SRC = \ $(TNANO_TASK_SRC) \ $(TNANO_MAIN_SRC) +# +# Sources for TeleDongle +# + +TBT_MAIN_SRC = \ + ao_telebt.c + +TBT_BASE_SRC = \ + $(ALTOS_SRC) \ + $(ALTOS_DRIVER_SRC) \ + $(TELE_RECEIVER_SRC) \ + $(TELE_COMMON_SRC) \ + $(SERIAL_DRIVER_SRC) \ + $(USB_DRIVER_SRC) \ + $(BTM_DRIVER_SRC) \ + $(DBG_SRC) \ + $(TBT_MAIN_SRC) + # # TI Dongle sources # diff --git a/src/ao.h b/src/ao.h index 89109fd9..226f9a22 100644 --- a/src/ao.h +++ b/src/ao.h @@ -107,6 +107,7 @@ ao_start_scheduler(void); #define AO_PANIC_REBOOT 8 /* Reboot failed */ #define AO_PANIC_FLASH 9 /* Invalid flash part (or wrong blocksize) */ #define AO_PANIC_USB 10 /* Trying to send USB packet while busy */ +#define AO_PANIC_BT 11 /* Communications with bluetooth device failed */ /* Stop the operating system, beeping and blinking the reason */ void @@ -403,6 +404,14 @@ ao_cmd_register(__code struct ao_cmds *cmds); void ao_cmd_init(void); +#if HAS_CMD_FILTER +/* + * Provided by an external module to filter raw command lines + */ +uint8_t +ao_cmd_filter(void); +#endif + /* * ao_dma.c */ @@ -903,6 +912,10 @@ ao_dbg_init(void); #endif #if HAS_SERIAL_1 +#ifndef USE_SERIAL_STDIN +#error Please define USE_SERIAL_STDIN +#endif + void ao_serial_rx1_isr(void) __interrupt 3; @@ -912,12 +925,24 @@ ao_serial_tx1_isr(void) __interrupt 14; char ao_serial_getchar(void) __critical; +#if USE_SERIAL_STDIN +char +ao_serial_pollchar(void) __critical; + +void +ao_serial_set_stdin(uint8_t stdin); +#endif + void ao_serial_putchar(char c) __critical; +void +ao_serial_drain(void) __critical; + #define AO_SERIAL_SPEED_4800 0 #define AO_SERIAL_SPEED_9600 1 -#define AO_SERIAL_SPEED_57600 2 +#define AO_SERIAL_SPEED_19200 2 +#define AO_SERIAL_SPEED_57600 3 void ao_serial_set_speed(uint8_t speed); @@ -1154,14 +1179,22 @@ struct ao_stdio { char (*pollchar)(void); void (*putchar)(char c) __reentrant; void (*flush)(void); + uint8_t echo; }; +extern __xdata struct ao_stdio ao_stdios[]; +extern __data int8_t ao_cur_stdio; +extern __data int8_t ao_num_stdios; + void flush(void); extern __xdata uint8_t ao_stdin_ready; -void +uint8_t +ao_echo(void); + +int8_t ao_add_stdio(char (*pollchar)(void), void (*putchar)(char) __reentrant, void (*flush)(void)) __reentrant; @@ -1332,4 +1365,13 @@ ao_packet_slave_stop(void); void ao_packet_slave_init(uint8_t enable); +/* ao_btm.c */ + +/* Shared by USB, so the USB code calls this function */ +void +ao_btm_isr(void); + +void +ao_btm_init(void); + #endif /* _AO_H_ */ diff --git a/src/ao_btm.c b/src/ao_btm.c new file mode 100644 index 00000000..db0ff6b0 --- /dev/null +++ b/src/ao_btm.c @@ -0,0 +1,253 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +int8_t ao_btm_stdio; +__xdata uint8_t ao_btm_connected; + +#define AO_BTM_MAX_REPLY 16 +__xdata char ao_btm_reply[AO_BTM_MAX_REPLY]; + +extern volatile __xdata struct ao_fifo ao_usart1_rx_fifo; + +/* + * Read a line of data from the serial port, truncating + * it after a few characters. + */ + +uint8_t +ao_btm_get_line(void) +{ + uint8_t ao_btm_reply_len = 0; + char c; + + for (;;) { + + while ((c = ao_serial_pollchar()) != AO_READ_AGAIN) { + if (ao_btm_reply_len < sizeof (ao_btm_reply)) + ao_btm_reply[ao_btm_reply_len++] = c; + if (c == '\r' || c == '\n') + goto done; + } + for (c = 0; c < 10; c++) { + ao_delay(AO_MS_TO_TICKS(10)); + if (!ao_fifo_empty(ao_usart1_rx_fifo)) + break; + } + if (c == 10) + goto done; + } +done: + for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);) + ao_btm_reply[c++] = '\0'; + return ao_btm_reply_len; +} + +/* + * Drain the serial port completely + */ +void +ao_btm_drain() +{ + while (ao_btm_get_line()) + ; +} + +/* + * Set the stdio echo for the bluetooth link + */ +void +ao_btm_echo(uint8_t echo) +{ + ao_stdios[ao_btm_stdio].echo = echo; +} + +/* + * Delay between command charaters; the BT module + * can't keep up with 57600 baud + */ + +void +ao_btm_putchar(char c) +{ + ao_serial_putchar(c); + ao_delay(1); +} + +/* + * Wait for the bluetooth device to return + * status from the previously executed command + */ +uint8_t +ao_btm_wait_reply(void) +{ + for (;;) { + ao_btm_get_line(); + if (!strncmp(ao_btm_reply, "OK", 2)) + return 1; + if (!strncmp(ao_btm_reply, "ERROR", 5)) + return -1; + if (ao_btm_reply[0] == '\0') + return 0; + } +} + +void +ao_btm_string(__code char *cmd) +{ + char c; + + while (c = *cmd++) + ao_btm_putchar(c); +} + +uint8_t +ao_btm_cmd(__code char *cmd) +{ + ao_btm_drain(); + ao_btm_string(cmd); + return ao_btm_wait_reply(); +} + +uint8_t +ao_btm_set_name(void) +{ + char sn[8]; + char *s = sn + 8; + char c; + int n; + ao_btm_string("ATN=TeleBT-"); + *--s = '\0'; + *--s = '\r'; + n = ao_serial_number; + do { + *--s = '0' + n % 10; + } while (n /= 10); + while ((c = *s++)) + ao_btm_putchar(c); + return ao_btm_wait_reply(); +} + +uint8_t +ao_btm_try_speed(uint8_t speed) +{ + ao_serial_set_speed(speed); + ao_btm_drain(); + (void) ao_btm_cmd("\rATE0\rATQ0\r"); + if (ao_btm_cmd("AT\r") == 1) + return 1; + return 0; +} + +/* + * A thread to initialize the bluetooth device and + * hang around to blink the LED when connected + */ +void +ao_btm(void) +{ + /* + * Wait for the bluetooth device to boot + */ + ao_delay(AO_SEC_TO_TICKS(3)); + + /* + * The first time we connect, the BTM-180 comes up at 19200 baud. + * After that, it will remember and come up at 57600 baud. So, see + * if it is already running at 57600 baud, and if that doesn't work + * then tell it to switch to 57600 from 19200 baud. + */ + while (!ao_btm_try_speed(AO_SERIAL_SPEED_57600)) { + ao_delay(AO_SEC_TO_TICKS(1)); + if (ao_btm_try_speed(AO_SERIAL_SPEED_19200)) + ao_btm_cmd("ATL4\r"); + ao_delay(AO_SEC_TO_TICKS(1)); + } + + /* Disable echo */ + ao_btm_cmd("ATE0\r"); + + /* Enable flow control */ + ao_btm_cmd("ATC1\r"); + + /* Set the reported name to something we can find on the host */ + ao_btm_set_name(); + + /* Turn off status reporting */ + ao_btm_cmd("ATQ1\r"); + + ao_btm_stdio = ao_add_stdio(ao_serial_pollchar, + ao_serial_putchar, + NULL); + ao_btm_echo(0); + + for (;;) { + while (!ao_btm_connected) + ao_sleep(&ao_btm_connected); + while (ao_btm_connected) { + ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20)); + ao_delay(AO_SEC_TO_TICKS(3)); + } + } +} + +__xdata struct ao_task ao_btm_task; + +void +ao_btm_check_link() __critical +{ + if (P2_1) { + ao_btm_connected = 0; + PICTL |= PICTL_P2ICON; + } else { + ao_btm_connected = 1; + PICTL &= ~PICTL_P2ICON; + } +} + +void +ao_btm_isr(void) +{ + if (P2IFG & (1 << 1)) { + ao_btm_check_link(); + ao_wakeup(&ao_btm_connected); + } + P2IFG = 0; +} + +void +ao_btm_init (void) +{ + ao_serial_init(); + ao_serial_set_speed(AO_SERIAL_SPEED_19200); + + /* + * Configure link status line + */ + + /* Set P2_1 to input, pull-down */ + P2DIR &= ~(1 << 1); + P2INP |= P2INP_MDP2_1_TRISTATE; + + /* Enable P2 interrupts */ + IEN2 |= IEN2_P2IE; + ao_btm_check_link(); + PICTL |= PICTL_P2IEN; + + ao_add_task(&ao_btm_task, ao_btm, "bt"); +} diff --git a/src/ao_cmd.c b/src/ao_cmd.c index 6007773c..23346c3d 100644 --- a/src/ao_cmd.c +++ b/src/ao_cmd.c @@ -21,7 +21,6 @@ __xdata uint16_t ao_cmd_lex_i; __xdata uint32_t ao_cmd_lex_u32; __xdata char ao_cmd_lex_c; __xdata enum ao_cmd_status ao_cmd_status; -static __xdata uint8_t lex_echo; #define CMD_LEN 32 @@ -41,7 +40,7 @@ static void readline(void) { __xdata char c; - if (lex_echo) + if (ao_echo()) put_string("> "); cmd_len = 0; for (;;) { @@ -50,7 +49,7 @@ readline(void) /* backspace/delete */ if (c == '\010' || c == '\177') { if (cmd_len != 0) { - if (lex_echo) + if (ao_echo()) put_string("\010 \010"); --cmd_len; } @@ -60,7 +59,7 @@ readline(void) /* ^U */ if (c == '\025') { while (cmd_len != 0) { - if (lex_echo) + if (ao_echo()) put_string("\010 \010"); --cmd_len; } @@ -72,18 +71,18 @@ readline(void) c = '\n'; if (c == '\n') { - if (lex_echo) + if (ao_echo()) putchar('\n'); break; } if (cmd_len >= CMD_LEN - 2) { - if (lex_echo) + if (ao_echo()) putchar('\007'); continue; } cmd_line[cmd_len++] = c; - if (lex_echo) + if (ao_echo()) putchar(c); } cmd_line[cmd_len++] = '\n'; @@ -198,7 +197,8 @@ static void echo(void) { ao_cmd_hex(); - lex_echo = ao_cmd_lex_i != 0; + if (ao_cmd_status == ao_cmd_success) + ao_stdios[ao_cur_stdio].echo = ao_cmd_lex_i != 0; } static void @@ -272,7 +272,6 @@ ao_cmd(void) __code struct ao_cmds * __xdata cs; void (*__xdata func)(void); - lex_echo = 1; for (;;) { readline(); ao_cmd_lex(); diff --git a/src/ao_packet_master.c b/src/ao_packet_master.c index 069bc5df..e721ffba 100644 --- a/src/ao_packet_master.c +++ b/src/ao_packet_master.c @@ -26,7 +26,7 @@ ao_packet_getchar(void) __critical break; if (ao_packet_master_sleeping) ao_wakeup(&ao_packet_master_sleeping); - ao_usb_flush(); + flush(); ao_sleep(&ao_stdin_ready); } return c; @@ -39,7 +39,7 @@ ao_packet_echo(void) __reentrant while (ao_packet_enable) { c = ao_packet_getchar(); if (c != AO_READ_AGAIN) - ao_usb_putchar(c); + putchar(c); } ao_exit(); } @@ -112,7 +112,7 @@ ao_packet_forward(void) __reentrant ao_set_monitor(0); ao_add_task(&ao_packet_task, ao_packet_master, "master"); ao_add_task(&ao_packet_echo_task, ao_packet_echo, "echo"); - while ((c = ao_usb_getchar()) != '~') { + while ((c = getchar()) != '~') { if (c == '\r') c = '\n'; ao_packet_putchar(c); } diff --git a/src/ao_pins.h b/src/ao_pins.h index 2161c5d2..324d7827 100644 --- a/src/ao_pins.h +++ b/src/ao_pins.h @@ -25,6 +25,7 @@ #define HAS_GPS 1 #define HAS_SERIAL_1 1 #define HAS_ADC 1 + #define USE_SERIAL_STDIN 0 #define HAS_EEPROM 1 #define USE_INTERNAL_FLASH 0 #define HAS_DBG 1 @@ -49,6 +50,7 @@ #define HAS_BEEP 1 #define HAS_GPS 1 #define HAS_SERIAL_1 1 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 1 #define HAS_EEPROM 1 #define USE_INTERNAL_FLASH 0 @@ -77,6 +79,7 @@ #define HAS_USB 1 #define HAS_BEEP 0 #define HAS_SERIAL_1 0 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 0 #define HAS_DBG 1 #define HAS_EEPROM 0 @@ -100,6 +103,7 @@ #define HAS_BEEP 0 #define HAS_GPS 0 #define HAS_SERIAL_1 0 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 1 #define HAS_EEPROM 1 #define USE_INTERNAL_FLASH 1 @@ -124,6 +128,7 @@ #define HAS_BEEP 0 #define HAS_GPS 0 #define HAS_SERIAL_1 0 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 1 #define HAS_EEPROM 1 #define USE_INTERNAL_FLASH 1 @@ -147,6 +152,7 @@ #define HAS_BEEP 1 #define HAS_GPS 1 #define HAS_SERIAL_1 1 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 1 #define HAS_DBG 0 #define HAS_EEPROM 1 @@ -173,6 +179,7 @@ #define HAS_USB 1 #define HAS_BEEP 0 #define HAS_SERIAL_1 0 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 0 #define HAS_DBG 0 #define HAS_EEPROM 0 @@ -195,6 +202,7 @@ #define HAS_USB 1 #define HAS_BEEP 0 #define HAS_SERIAL_1 0 + #define USE_SERIAL_STDIN 0 #define HAS_ADC 0 #define HAS_DBG 1 #define HAS_EEPROM 0 @@ -211,6 +219,30 @@ #define HAS_IGNITE 0 #endif +#if defined(TELEBT_V_0_0) + #define HAS_FLIGHT 0 + #define HAS_USB 1 + #define HAS_BEEP 0 + #define HAS_SERIAL_1 1 + #define USE_SERIAL_STDIN 1 + #define HAS_ADC 0 + #define HAS_DBG 1 + #define HAS_EEPROM 0 + #define HAS_BTM 1 + #define DBG_ON_P1 0 + #define DBG_ON_P0 1 + #define IGNITE_ON_P2 0 + #define IGNITE_ON_P0 0 + #define PACKET_HAS_MASTER 1 + #define PACKET_HAS_SLAVE 0 + #define AO_LED_RED 2 + #define AO_LED_GREEN 1 + #define LEDS_AVAILABLE (AO_LED_RED|AO_LED_GREEN) + #define SPI_CS_ON_P1 1 + #define SPI_CS_ON_P0 0 + #define HAS_IGNITE 0 +#endif + #if DBG_ON_P1 #define DBG_CLOCK (1 << 4) /* mi0 */ @@ -271,6 +303,10 @@ #error Please define HAS_SERIAL_1 #endif +#ifndef USE_SERIAL_STDIN +#error Please define USE_SERIAL_STDIN +#endif + #ifndef HAS_ADC #error Please define HAS_ADC #endif diff --git a/src/ao_serial.c b/src/ao_serial.c index dd383fca..9c0b798d 100644 --- a/src/ao_serial.c +++ b/src/ao_serial.c @@ -26,6 +26,9 @@ ao_serial_rx1_isr(void) __interrupt 3 if (!ao_fifo_full(ao_usart1_rx_fifo)) ao_fifo_insert(ao_usart1_rx_fifo, U1DBUF); ao_wakeup(&ao_usart1_rx_fifo); +#if USE_SERIAL_STDIN + ao_wakeup(&ao_stdin_ready); +#endif } static __xdata uint8_t ao_serial_tx1_started; @@ -50,8 +53,6 @@ ao_serial_tx1_isr(void) __interrupt 14 ao_wakeup(&ao_usart1_tx_fifo); } -static __pdata serial_echo; - char ao_serial_getchar(void) __critical { @@ -59,16 +60,21 @@ ao_serial_getchar(void) __critical while (ao_fifo_empty(ao_usart1_rx_fifo)) ao_sleep(&ao_usart1_rx_fifo); ao_fifo_remove(ao_usart1_rx_fifo, c); - if (serial_echo) { - printf("%02x ", ((int) c) & 0xff); - if (c >= ' ') - putchar(c); - putchar('\n'); - flush(); - } return c; } +#if USE_SERIAL_STDIN +char +ao_serial_pollchar(void) __critical +{ + char c; + if (ao_fifo_empty(ao_usart1_rx_fifo)) + return AO_READ_AGAIN; + ao_fifo_remove(ao_usart1_rx_fifo,c); + return c; +} +#endif + void ao_serial_putchar(char c) __critical { @@ -78,25 +84,13 @@ ao_serial_putchar(char c) __critical ao_serial_tx1_start(); } -static void +void ao_serial_drain(void) __critical { while (!ao_fifo_empty(ao_usart1_tx_fifo)) ao_sleep(&ao_usart1_tx_fifo); } -static void -monitor_serial(void) -{ - ao_cmd_hex(); - serial_echo = ao_cmd_lex_i != 0; -} - -__code struct ao_cmds ao_serial_cmds[] = { - { monitor_serial, "M \0Monitor serial data" }, - { 0, NULL }, -}; - static const struct { uint8_t baud; uint8_t gcr; @@ -109,6 +103,10 @@ static const struct { /* .baud = */ 163, /* .gcr = */ (8 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB }, + /* [AO_SERIAL_SPEED_19200] = */ { + /* .baud = */ 163, + /* .gcr = */ (9 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB + }, /* [AO_SERIAL_SPEED_57600] = */ { /* .baud = */ 59, /* .gcr = */ (11 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB @@ -121,6 +119,7 @@ ao_serial_set_speed(uint8_t speed) ao_serial_drain(); if (speed > AO_SERIAL_SPEED_57600) return; + U1UCR |= UxUCR_FLUSH; U1BAUD = ao_serial_speeds[speed].baud; U1GCR = ao_serial_speeds[speed].gcr; } @@ -131,7 +130,8 @@ ao_serial_init(void) /* Set up the USART pin assignment */ PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2; - /* ee has already set the P2SEL bits */ + P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI2P1_MASK)) | + (P2SEL_PRI3P1_USART1 | P2SEL_PRI2P1_USART1); /* Make the USART pins be controlled by the USART */ P1SEL |= (1 << 6) | (1 << 7); @@ -145,7 +145,7 @@ ao_serial_init(void) /* Reasonable serial parameters */ U1UCR = (UxUCR_FLUSH | UxUCR_FLOW_DISABLE | - UxUCR_D9_ODD_PARITY | + UxUCR_D9_EVEN_PARITY | UxUCR_BIT9_8_BITS | UxUCR_PARITY_DISABLE | UxUCR_SPB_1_STOP_BIT | @@ -154,6 +154,4 @@ ao_serial_init(void) IEN0 |= IEN0_URX1IE; IEN2 |= IEN2_UTX1IE; - - ao_cmd_register(&ao_serial_cmds[0]); } diff --git a/src/ao_stdio.c b/src/ao_stdio.c index 6e1f5eff..6b890832 100644 --- a/src/ao_stdio.c +++ b/src/ao_stdio.c @@ -21,25 +21,25 @@ * Basic I/O functions to support SDCC stdio package */ -#define AO_NUM_STDIOS 2 +#define AO_NUM_STDIOS (HAS_USB + PACKET_HAS_SLAVE + USE_SERIAL_STDIN) -static __xdata struct ao_stdio stdios[AO_NUM_STDIOS]; -static __data int8_t ao_cur_stdio; -static __data int8_t ao_num_stdios; +__xdata struct ao_stdio ao_stdios[AO_NUM_STDIOS]; +__data int8_t ao_cur_stdio; +__data int8_t ao_num_stdios; void putchar(char c) { if (c == '\n') - (*stdios[ao_cur_stdio].putchar)('\r'); - (*stdios[ao_cur_stdio].putchar)(c); + (*ao_stdios[ao_cur_stdio].putchar)('\r'); + (*ao_stdios[ao_cur_stdio].putchar)(c); } void flush(void) { - if (stdios[ao_cur_stdio].flush) - stdios[ao_cur_stdio].flush(); + if (ao_stdios[ao_cur_stdio].flush) + ao_stdios[ao_cur_stdio].flush(); } __xdata uint8_t ao_stdin_ready; @@ -51,7 +51,7 @@ getchar(void) __reentrant __critical int8_t stdio = ao_cur_stdio; for (;;) { - c = stdios[stdio].pollchar(); + c = ao_stdios[stdio].pollchar(); if (c != AO_READ_AGAIN) break; if (++stdio == ao_num_stdios) @@ -63,15 +63,22 @@ getchar(void) __reentrant __critical return c; } -void +uint8_t +ao_echo(void) +{ + return ao_stdios[ao_cur_stdio].echo; +} + +int8_t ao_add_stdio(char (*pollchar)(void), void (*putchar)(char), void (*flush)(void)) __reentrant { if (ao_num_stdios == AO_NUM_STDIOS) ao_panic(AO_PANIC_STDIO); - stdios[ao_num_stdios].pollchar = pollchar; - stdios[ao_num_stdios].putchar = putchar; - stdios[ao_num_stdios].flush = flush; - ao_num_stdios++; + ao_stdios[ao_num_stdios].pollchar = pollchar; + ao_stdios[ao_num_stdios].putchar = putchar; + ao_stdios[ao_num_stdios].flush = flush; + ao_stdios[ao_num_stdios].echo = 1; + return ao_num_stdios++; } diff --git a/src/ao_telebt.c b/src/ao_telebt.c new file mode 100644 index 00000000..295f0cec --- /dev/null +++ b/src/ao_telebt.c @@ -0,0 +1,41 @@ +/* + * Copyright © 2011 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao.h" + +void +main(void) +{ + ao_clock_init(); + + /* Turn on the LED until the system is stable */ + ao_led_init(LEDS_AVAILABLE); + ao_led_on(AO_LED_RED); + ao_timer_init(); + ao_cmd_init(); + ao_usb_init(); + ao_monitor_init(AO_LED_GREEN, TRUE); + ao_rssi_init(AO_LED_RED); + ao_radio_init(); + ao_packet_master_init(); + ao_btm_init(); +#if HAS_DBG + ao_dbg_init(); +#endif + ao_config_init(); + ao_start_scheduler(); +} diff --git a/src/ao_usb.c b/src/ao_usb.c index b4e3f1fe..ece6756a 100644 --- a/src/ao_usb.c +++ b/src/ao_usb.c @@ -46,6 +46,7 @@ void ao_usb_isr(void) __interrupt 6 { USBIF = 0; + IRCON2 &= ~IRCON2_USBIF; ao_usb_iif |= USBIIF; if (ao_usb_iif & 1) ao_wakeup(&ao_usb_task); @@ -57,6 +58,9 @@ ao_usb_isr(void) __interrupt 6 if (USBCIF & USBCIF_RSTIF) ao_usb_set_interrupts(); +#if HAS_BTM + ao_btm_isr(); +#endif } struct ao_usb_setup { diff --git a/src/cc1111.h b/src/cc1111.h index effb1a68..a07490e5 100644 --- a/src/cc1111.h +++ b/src/cc1111.h @@ -565,14 +565,19 @@ sfr at 0xF5 P2SEL; #define P2SEL_PRI3P1_MASK (1 << 6) #define P2SEL_PRI2P1_USART1 (0 << 5) #define P2SEL_PRI2P1_TIMER3 (1 << 5) +#define P2SEL_PRI2P1_MASK (1 << 5) #define P2SEL_PRI1P1_TIMER1 (0 << 4) #define P2SEL_PRI1P1_TIMER4 (1 << 4) +#define P2SEL_PRI1P1_MASK (1 << 4) #define P2SEL_PRI0P1_USART0 (0 << 3) #define P2SEL_PRI0P1_TIMER1 (1 << 3) +#define P2SEL_PRI0P1_MASK (1 << 3) #define P2SEL_SELP2_4_GPIO (0 << 2) #define P2SEL_SELP2_4_PERIPHERAL (1 << 2) +#define P2SEL_SELP2_4_MASK (1 << 2) #define P2SEL_SELP2_3_GPIO (0 << 1) #define P2SEL_SELP2_3_PERIPHERAL (1 << 1) +#define P2SEL_SELP2_3_MASK (1 << 1) #define P2SEL_SELP2_0_GPIO (0 << 0) #define P2SEL_SELP2_0_PERIPHERAL (1 << 0) #define P2SEL_SELP2_0_MASK (1 << 0) @@ -657,6 +662,14 @@ sfr at 0x8B P2IFG; #define P0IFG_USB_RESUME (1 << 7) +sfr at 0x8C PICTL; +#define PICTL_P2IEN (1 << 5) +#define PICTL_P0IENH (1 << 4) +#define PICTL_P0IENL (1 << 3) +#define PICTL_P2ICON (1 << 2) +#define PICTL_P1ICON (1 << 1) +#define PICTL_P0ICON (1 << 0) + /* GPIO pins */ sfr at 0x80 P0; diff --git a/src/telebt-v0.0/.gitignore b/src/telebt-v0.0/.gitignore new file mode 100644 index 00000000..1acfbfcc --- /dev/null +++ b/src/telebt-v0.0/.gitignore @@ -0,0 +1,2 @@ +telebt-* +ao_product.h diff --git a/src/telebt-v0.0/.sdcdbrc b/src/telebt-v0.0/.sdcdbrc new file mode 100644 index 00000000..710b4a2f --- /dev/null +++ b/src/telebt-v0.0/.sdcdbrc @@ -0,0 +1 @@ +--directory=.. diff --git a/src/telebt-v0.0/Makefile.defs b/src/telebt-v0.0/Makefile.defs new file mode 100644 index 00000000..f0bb5e0c --- /dev/null +++ b/src/telebt-v0.0/Makefile.defs @@ -0,0 +1,8 @@ +PROG = telebt-v0.0-$(VERSION).ihx + +SRC = \ + $(TBT_BASE_SRC) + +PRODUCT=TeleBT-v0.0 +PRODUCT_DEF=-DTELEBT_V_0_0 +IDPRODUCT=0x000e