source: thomson/elec/CrO2/software/device.cpp@ e43c8d0

main
Last change on this file since e43c8d0 was e43c8d0, checked in by Adrien Destugues <pulkomandy@…>, 12 years ago

Some cleanup.

git-svn-id: svn://localhost/thomson@26 85ae3b6b-dc8f-4344-a89d-598714f2e4e5

  • Property mode set to 100644
File size: 4.6 KB
Line 
1/* CrO2 datassette emulator
2 * Copyright 2012, Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
3 *
4 * Distributed under the terms of the MIT licence.
5 *
6 * Handles device communication through libusb
7 */
8
9#include "device.h"
10#include "k5.h"
11
12#include <typeinfo>
13
14bool Device::initOnce = false;
15Device* Device::instance = NULL;
16
17const uint32_t Device::vid = 0x16C0;
18const uint32_t Device::pid = 0x05DC;
19const char* Device::vendor = "pulkomandy.ath.cx";
20const char* Device::product = "CrO2";
21
22/* These are the vendor specific commands implemented by our USB device */
23#define PSCMD_CONFIG 0
24#define PSCMD_GET 1
25#define PSCMD_PUT 2
26#define PSCMD_STATUS 3
27
28
29// Gets the device instance. Throws an error message if something bad happens.
30Device& Device::getDevice() throw(const char*)
31{
32 // If Device constructor throws an exception, instance is not initialized.
33 // So, the constructor will be called again at next getDevice call, until
34 // it succeeds.
35 if (instance == NULL)
36 instance = new Device();
37
38 return *instance;
39}
40
41
42/* USB device lookup by VID and PID, then Vendor and Product strings, as we use
43 * V-USB shared ID. */
44static int usbGetStringAscii(usb_dev_handle *dev, int index, int langid,
45 char *buf, int buflen)
46{
47char buffer[256];
48int rval, i;
49
50 if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
51 (USB_DT_STRING << 8) + index, langid, buffer, sizeof(buffer), 1000)) < 0)
52 return rval;
53 if(buffer[1] != USB_DT_STRING)
54 return 0;
55 if((unsigned char)buffer[0] < rval)
56 rval = (unsigned char)buffer[0];
57 rval /= 2;
58 /* lossy conversion to ISO Latin1 */
59 for(i=1;i<rval;i++){
60 if(i > buflen) /* destination buffer overflow */
61 break;
62 buf[i-1] = buffer[2 * i];
63 if(buffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
64 buf[i-1] = '?';
65 }
66 buf[i-1] = 0;
67 return i-1;
68}
69
70Device::Device() throw(const char*)
71{
72 handle = NULL;
73
74 if (!initOnce)
75 {
76 initOnce = true;
77 usb_init();
78 }
79
80 usb_find_busses();
81 usb_find_devices();
82
83 for(struct usb_bus* bus=usb_get_busses(); bus; bus=bus->next)
84 {
85 for(struct usb_device* dev=bus->devices; dev; dev=dev->next)
86 {
87 if(dev->descriptor.idVendor != vid || dev->descriptor.idProduct != pid)
88 continue;
89
90 // Found device with correct VID and PID. Now try to match the
91 // vendor and product strings
92 char string[256];
93 int len;
94 handle = usb_open(dev);
95 /* we need to open the device in order to query strings */
96 if(!handle){
97 continue;
98 }
99 /* now check whether the names match: */
100 len = usbGetStringAscii(handle, dev->descriptor.iManufacturer,
101 0x0409, string, sizeof(string));
102 if(len >= 0)
103 {
104 if(strcmp(string, vendor) == 0){
105 len = usbGetStringAscii(handle, dev->descriptor.iProduct,
106 0x0409, string, sizeof(string));
107 if(len >= 0){
108 if(strcmp(string, product) == 0)
109 break;
110 }
111 }
112 }
113 usb_close(handle);
114 handle = NULL;
115 }
116 if(handle)
117 break;
118 }
119
120 // At this point, either we have found a device and handle is pointing to it,
121 // or we failed and handle is NULL.
122 if (!handle)
123 throw "Device not found. Is the USB cable plugged correctly?";
124}
125
126
127Device::~Device()
128{
129 usb_close(handle);
130}
131
132
133int Device::read(uint8_t* buffer, size_t max)
134{
135 return usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
136 PSCMD_GET, 0,0, (char*)buffer, max, 5000);
137
138}
139
140
141int Device::write(const uint8_t* buffer, size_t size, int blktype)
142{
143 int rqtype = (size == 0) ? USB_ENDPOINT_IN:USB_ENDPOINT_OUT;
144 return usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | rqtype,
145 PSCMD_PUT, blktype,0 /*checksum*/, (char*)buffer, size, 5000);
146
147}
148
149
150void Device::write(const Tape& file) throw (const char*)
151{
152 for (int k = 0; k < file.getBlockCount(); k++)
153 {
154 // wait for motor on
155 while (getStatus() & 8)
156 Sleep(1000);
157
158 const Tape::Block& block = file.getBlock(k);
159 try {
160 const K5::Block& moblock = dynamic_cast<const K5::Block&>(block);
161 write(moblock.data, moblock.length - 1, moblock.type);
162 // TODO error handling on write
163 } catch (std::bad_cast x) {
164 throw "Only MO5 files supported so far. Sorry!";
165 };
166
167 // TODO wait for correct time (read status from usb OR compute from size+type)
168 Sleep(1400);
169 }
170}
171
172uint8_t Device::getStatus()
173{
174 uint8_t status;
175 usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
176 PSCMD_STATUS, 0,0, (char*)&status, 1, 5000);
177 // TODO handle errors (return value)
178 return status;
179}
Note: See TracBrowser for help on using the repository browser.