forked from Gotos/CuteCapture
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dscapture.c
153 lines (130 loc) · 4.54 KB
/
dscapture.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include "dscapture.h"
#include <stdio.h>
#include <memory.h>
#include <libusb-1.0/libusb.h>
enum {
USBDS_VID = 0x16D0,
USBDS_PID = 0x0647,
DEFAULT_CONFIGURATION = 1,
CAPTURE_INTERFACE = 0,
CONTROL_TIMEOUT = 500,
BULK_TIMEOUT = 500,
EP2_IN = 2 | LIBUSB_ENDPOINT_IN,
//device vendor requests:
CMDIN_STATUS = 0x31,
// Returns device status:
// struct {
// u32 framecount, //free running frame counter
// u8 lcd_on,
// u8 capture_in_progress,
// }
CMDIN_FRAMEINFO = 0x30,
// Returns record of last captured frame:
// struct {
// u8 bitmap[48], //bitmap of lines sent (1 bit per half-line)
// u32 frame, //frame number
// u8 valid, //0 if capture timed out (LCD is inactive)
// }
CMDOUT_CAPTURE_START = 0x30,
// capture new frame
CMDOUT_CAPTURE_STOP = 0x31,
// stop capture in progress and reset frame counter to 0
};
static libusb_device_handle *dev = NULL;
static bool init=false;
// Open DS Capture device (call once)
bool capture_init() {
if(libusb_init(NULL)!=LIBUSB_SUCCESS)
return false;
init=true;
dev = libusb_open_device_with_vid_pid(NULL, USBDS_VID, USBDS_PID);
if(!dev)
goto err;
if(libusb_set_configuration(dev, DEFAULT_CONFIGURATION) != LIBUSB_SUCCESS)
goto err;
if(libusb_claim_interface(dev, CAPTURE_INTERFACE) != LIBUSB_SUCCESS)
goto err;
return true;
err:
capture_deinit();
return false;
}
// Close DS Capture device (call before exiting)
void capture_deinit() {
if(dev) {
libusb_release_interface(dev, CAPTURE_INTERFACE);
libusb_close(dev);
dev=NULL;
}
if(init) {
libusb_exit(NULL);
init=false;
}
}
// Read vendor request from control endpoint. Returns bytes transferred (<0 = libusb error)
static int vend_in(uint8_t bRequest, /* uint16_t wValue, uint16_t wIndex, */ uint16_t wLength, uint8_t *buf) {
return libusb_control_transfer(dev, (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN), bRequest, 0, 0, buf, wLength, CONTROL_TIMEOUT);
}
// Write vendor request to control endpoint. Returns bytes transferred (<0 = libusb error)
static int vend_out(uint8_t bRequest, uint16_t wValue, /* uint16_t wIndex, */ uint16_t wLength, uint8_t *buf) {
return libusb_control_transfer(dev, (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT), bRequest, wValue, 0, buf, wLength, CONTROL_TIMEOUT);
}
// Read from bulk endpoint. Returns libusb error code
static int bulk_in(uint8_t *buf, int length, int *transferred) {
return libusb_bulk_transfer(dev, EP2_IN, buf, length, transferred, BULK_TIMEOUT);
}
// frameBuf is 256x384 BGR16
bool capture_grabFrame(uint16_t *frameBuf) {
enum {
infoSize=64,
lcdWidth=256,
lcdHeight=192,
frameSize=1024*lcdHeight,
};
static uint16_t tmpBuf[frameSize/sizeof(uint16_t)];
static uint8_t frameInfo[infoSize];
uint8_t dummy;
if(vend_out(CMDOUT_CAPTURE_START, 0, 0, &dummy) < 0)
return false;
//DS sends data until end of frame, then sends 0-length packets.
int transferred;
int result;
int bytesIn=0;
uint8_t *p=(uint8_t*)tmpBuf;
do {
result = bulk_in(p, frameSize-bytesIn, &transferred);
if(result==LIBUSB_SUCCESS) {
bytesIn+=transferred;
p+=transferred;
}
} while(bytesIn<frameSize && result==LIBUSB_SUCCESS && transferred>0);
if(result!=LIBUSB_SUCCESS)
return false;
if(vend_in(CMDIN_FRAMEINFO, infoSize, frameInfo)<0)
return false;
if((frameInfo[0]&3)!=3) //First line is valid. Should always be true, simplifies filling code below.
return false;
if(!frameInfo[52]) //LCD was off
return false;
//de-interleave pixels and deal with lost packets
int i;
int line;
uint16_t *src=tmpBuf;
uint16_t *dst=frameBuf;
for(line=0; line<lcdHeight*2; ++line) {
if(frameInfo[line>>3] & (1<<(line&7))) {
for(i=0; i<lcdWidth/2; ++i) {
dst[0]=src[1];
dst[lcdWidth*lcdHeight]=src[0];
dst++;
src+=2;
}
} else { //line missing, copy previous
memcpy(dst, dst-256, 256);
memcpy(dst+256*192, dst+256*191, 256);
dst+=128;
//linesLost++;
}
}
return true;
}