diff options
41 files changed, 1852 insertions, 505 deletions
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 <keithp@keithp.com> + * + * 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 <keithp@keithp.com> + * + * 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> { +	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 <keithp@keithp.com> + * + * 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<AltosBTDevice> { +	LinkedList<AltosBTDevice>	devices = new LinkedList<AltosBTDevice>(); +	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<AltosBTDevice> iterator() { +		return devices.iterator(); +	} + +	private void flush() { +		AltosPreferences.flush_preferences(); +	} + +	public void set(Iterable<AltosBTDevice> 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<AltosBTDevice>(); +		for (AltosBTDevice new_device : new_devices) { +			devices.add(new_device); +			set_address(new_device.getName(), new_device.getAddr()); +		} +		flush(); +	} + +	public List<AltosDevice> list(int product) { +		LinkedList<AltosDevice>	list = new LinkedList<AltosDevice>(); +		for (AltosBTDevice device : devices) { +			if (device.matchProduct(product)) +				list.add(device); +		} +		return list; +	} + +	public AltosBTKnown() { +		devices = new LinkedList<AltosBTDevice>(); +		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 <keithp@keithp.com> + * + * 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<AltosBTDevice> { +	LinkedBlockingQueue<AltosBTDevice> found_devices; +	Frame frame; +	LinkedList<ActionListener> listeners; +	AltosBTKnown	bt_known; + +	class DeviceList extends JList implements Iterable<AltosBTDevice> { +		LinkedList<AltosBTDevice> 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<AltosBTDevice> iterator() { +			return devices.iterator(); +		} + +		public java.util.List<AltosBTDevice> selected_list() { +			java.util.LinkedList<AltosBTDevice> l = new java.util.LinkedList<AltosBTDevice>(); +			Object[] a = getSelectedValues(); +			for (int i = 0; i < a.length; i++) +				l.add((AltosBTDevice)a[i]); +			return l; +		} + +		public DeviceList() { +			devices = new LinkedList<AltosBTDevice>(); +			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<AltosBTDevice> 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<ActionListener>(); + +		found_devices = new LinkedBlockingQueue<AltosBTDevice>(); + +		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 <keithp@keithp.com> + * Copyright © 2011 Keith Packard <keithp@keithp.com>   *   * 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<AltosDevice> device_list = new ArrayList<AltosDevice>(); -		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<AltosDevice>	usb_devices = AltosUSBDevice.list(product); +		java.util.List<AltosDevice>	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<String>(); -		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","<undefined>")), @@ -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<AltosDevice> 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 <keithp@keithp.com> + * + * 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<AltosDevice> list(int product) { +		if (!Altos.load_library()) +			return null; + +		SWIGTYPE_p_altos_list list = libaltos.altos_list_start(); + +		ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>(); +		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 <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <termios.h> +#include <errno.h> + +#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 <poll.h> +#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 <stdio.h>  #include <stdlib.h>  #include <string.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h>  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 <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <termios.h> -#include <errno.h> - -#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 <poll.h> -#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  # @@ -230,6 +238,24 @@ TNANO_BASE_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  #  TI_MAIN_SRC = \ @@ -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 <keithp@keithp.com> + * + * 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 <enable>\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 <keithp@keithp.com> + * + * 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  | 
