2 Copyright 2010, jimmikaelkael
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
20 #define MODNAME "hdldsvr"
21 IRX_ID(MODNAME
, 1, 1);
23 struct irx_export_table _exp_hdldsvr
;
25 #define TCP_SERVER_PORT 45233
26 #define UDP_SERVER_PORT 45233
29 #define CMD_STAT 0x73746174 // 'stat'; get HDD size in KB
30 #define CMD_READ 0x72656164 // 'read'; read sectors from HDD
31 #define CMD_WRIT 0x77726974 // 'writ'; write sectors to HDD
32 #define CMD_WRIS 0x77726973 // 'wris'; get last write status
33 #define CMD_FLSH 0x666c7368 // 'flsh'; flush write buff
34 #define CMD_POWX 0x706f7778 // 'powx'; poweroff system
36 #define HDD_SECTOR_SIZE 512 // HDD sector size in bytes
37 #define NET_NUM_SECTORS 2048 // max # of sectors to move via network
39 #define STATUS_AVAIL 0 // expecting command
40 #define STATUS_BUSY_RESPOND 1 // sending back response (and readen data)
41 #define STATUS_BUSY_WRITE 2 // expecting write data
42 #define STATUS_WRITE_STAT 3 // sending back write stat response
44 #define SETBIT(mask, bit) (mask)[(bit) / 32] |= 1 << ((bit) % 32)
45 #define GETBIT(mask, bit) ((mask)[(bit) / 32] & (1 << ((bit) % 32)))
47 #define APA_DEVCTL_MAX_SECTORS 0x00004801 // max partition size(in sectors)
48 #define APA_DEVCTL_TOTAL_SECTORS 0x00004802
49 #define APA_DEVCTL_IDLE 0x00004803
50 #define APA_DEVCTL_FLUSH_CACHE 0x00004804
51 #define APA_DEVCTL_SWAP_TMP 0x00004805
52 #define APA_DEVCTL_DEV9_SHUTDOWN 0x00004806
53 #define APA_DEVCTL_STATUS 0x00004807
54 #define APA_DEVCTL_FORMAT 0x00004808
55 #define APA_DEVCTL_SMART_STAT 0x00004809
56 #define APA_DEVCTL_GETTIME 0x00006832
57 #define APA_DEVCTL_SET_OSDMBR 0x00006833 // arg = hddSetOsdMBR_t
58 #define APA_DEVCTL_GET_SECTOR_ERROR 0x00006834
59 #define APA_DEVCTL_GET_ERROR_PART_NAME 0x00006835 // bufp = namebuffer[0x20]
60 #define APA_DEVCTL_ATA_READ 0x00006836 // arg = hddAtaTransfer_t
61 #define APA_DEVCTL_ATA_WRITE 0x00006837 // arg = hddAtaTransfer_t
62 #define APA_DEVCTL_SCE_IDENTIFY_DRIVE 0x00006838 // bufp = buffer for atadSceIdentifyDrive
63 #define APA_DEVCTL_IS_48BIT 0x00006840
64 #define APA_DEVCTL_SET_TRANSFER_MODE 0x00006841
65 #define APA_DEVCTL_ATA_IOP_WRITE 0x00006842
68 void tcp_server_thread(void *args
);
69 void udp_server_thread(void *args
);
71 static int tcp_server_tid
, udp_server_tid
;
72 static int udp_mutex
= -1;
74 static u8 tcp_buf
[CMD_SIZE
+ HDD_SECTOR_SIZE
*2] __attribute__((aligned(64)));
75 static u8 udp_buf
[8 + HDD_SECTOR_SIZE
*2] __attribute__((aligned(64)));
77 struct tcp_packet_header
{
82 } __attribute__((packed
));
85 struct tcp_packet_header hdr
;
86 u8 data
[HDD_SECTOR_SIZE
*2];
87 } tcp_packet_t
__attribute__((packed
));
90 u8 data
[HDD_SECTOR_SIZE
* 2];
93 } udp_packet_t
__attribute__((packed
));
101 u32 bitmask
[(NET_NUM_SECTORS
+ 31) / 32];
102 u32 bitmask_copy
[(NET_NUM_SECTORS
+ 31) / 32]; // endian-neutral
103 u8 buffer
[HDD_SECTOR_SIZE
* (NET_NUM_SECTORS
+ 1)]; // 1MB on IOP !!! Huge !
106 static struct stats gstats
__attribute__((aligned(64)));
108 //-------------------------------------------------------------------------
109 int _start(int argc
, char** argv
)
111 iop_thread_t thread_param
;
114 RegisterLibraryEntries(&_exp_hdldsvr
);
116 // create a locked udp mutex
117 udp_mutex
= CreateMutex(IOP_MUTEX_LOCKED
);
119 // create & start the tcp thread
120 thread_param
.attr
= TH_C
;
121 thread_param
.thread
= (void *)tcp_server_thread
;
122 thread_param
.priority
= 0x64;
123 thread_param
.stacksize
= 0x1000;
124 thread_param
.option
= 0;
126 tcp_server_tid
= CreateThread(&thread_param
);
128 StartThread(tcp_server_tid
, 0);
130 // create & start the udp thread
131 thread_param
.attr
= TH_C
;
132 thread_param
.thread
= (void *)udp_server_thread
;
133 thread_param
.priority
= 0x64;
134 thread_param
.stacksize
= 0x1000;
135 thread_param
.option
= 0;
137 udp_server_tid
= CreateThread(&thread_param
);
139 StartThread(udp_server_tid
, 0);
141 return MODULE_RESIDENT_END
;
144 //-------------------------------------------------------------------------
148 DeleteThread(tcp_server_tid
);
149 DeleteThread(udp_server_tid
);
152 DeleteSema(udp_mutex
);
157 //-------------------------------------------------------------------------
158 static int ack_tcp_command(int sock
, void *buf
, int bsize
)
162 // acknowledge a received command
164 gstats
.status
= STATUS_BUSY_RESPOND
;
166 r
= lwip_send(sock
, buf
, bsize
, 0);
168 gstats
.status
= STATUS_AVAIL
;
173 //-------------------------------------------------------------------------
174 static void reject_tcp_command(int sock
, char *error_string
)
176 // reject a received command
178 gstats
.status
= STATUS_BUSY_RESPOND
;
180 lwip_send(sock
, error_string
, strlen(error_string
), 0);
182 gstats
.status
= STATUS_AVAIL
;
184 devctl("hdd0:", APA_DEVCTL_FLUSH_CACHE
, NULL
, 0, NULL
, 0);
187 //-------------------------------------------------------------------------
188 static int check_datas(u32 command
, u32 start_sector
, u32 num_sectors
)
190 // check if all write data is here
192 register int i
, got_all_datas
= 1;
194 // wait that udp mutex is unlocked
197 for (i
=0; i
<num_sectors
; ++i
) {
198 if (!GETBIT(gstats
.bitmask
, i
)) {
204 if (!got_all_datas
) // some parts are missing; should ask for retransmit
207 // all data here -- we can commit
208 gstats
.command
= command
;
212 //-------------------------------------------------------------------------
213 static int handle_tcp_client(int tcp_client_socket
)
215 register int r
, i
, size
;
217 tcp_packet_t
*pkt
= (tcp_packet_t
*)tcp_buf
;
221 // receive incoming packets
222 size
= lwip_recv(tcp_client_socket
, &tcp_buf
[0], sizeof(tcp_buf
), 0);
226 // check if valid command size
227 if (size
!= CMD_SIZE
) {
228 reject_tcp_command(tcp_client_socket
, "handle_recv: invalid packet received");
232 // don't accept 'wris' without previous 'writ'
233 if ((gstats
.status
== STATUS_AVAIL
) && (pkt
->hdr
.command
== CMD_WRIS
)) {
234 reject_tcp_command(tcp_client_socket
, "write stat denied");
237 else if ((!(gstats
.status
== STATUS_BUSY_WRITE
) && (pkt
->hdr
.command
== CMD_WRIS
))) {
238 reject_tcp_command(tcp_client_socket
, "busy");
242 switch (pkt
->hdr
.command
) {
244 // ------------------------------------------------
246 // ------------------------------------------------
250 r
= ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(struct tcp_packet_header
));
252 reject_tcp_command(tcp_client_socket
, "init_write b0rked");
256 gstats
.status
= STATUS_BUSY_WRITE
;
257 gstats
.command
= pkt
->hdr
.command
;
258 gstats
.start_sector
= pkt
->hdr
.start_sector
;
259 gstats
.num_sectors
= pkt
->hdr
.num_sectors
;
261 // set-up buffer and clear bitmask
262 gstats
.data
= (u8
*)(((long)&gstats
.buffer
[HDD_SECTOR_SIZE
- 1]) & ~(HDD_SECTOR_SIZE
- 1));
263 mips_memset (gstats
.bitmask
, 0, sizeof (gstats
.bitmask
));
268 // ------------------------------------------------
270 // ------------------------------------------------
272 if ((pkt
->hdr
.start_sector
!= gstats
.start_sector
) || (pkt
->hdr
.num_sectors
!= gstats
.num_sectors
)) {
273 reject_tcp_command(tcp_client_socket
, "invalid write stat");
276 r
= check_datas(pkt
->hdr
.command
, pkt
->hdr
.start_sector
, pkt
->hdr
.num_sectors
);
279 // means we need retransmission of some datas so we send bitmask stats to the client
280 u32
*out
= gstats
.bitmask_copy
;
281 for (i
=0; i
<(NET_NUM_SECTORS
+31)/32; ++i
)
282 out
[i
] = gstats
.bitmask
[i
];
284 mips_memcpy(pkt
->data
, (void *)out
, sizeof(gstats
.bitmask_copy
));
287 r
= ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(struct tcp_packet_header
)+sizeof(gstats
.bitmask
));
289 reject_tcp_command(tcp_client_socket
, "init_write_stat failed");
294 gstats
.status
= STATUS_BUSY_WRITE
;
297 pkt
->hdr
.result
= pkt
->hdr
.num_sectors
;
300 // fake write, we read instead
301 *(u32
*)&args
[0] = pkt
->hdr
.start_sector
;
302 *(u32
*)&args
[4] = pkt
->hdr
.num_sectors
;
303 devctl("hdd0:", APA_DEVCTL_ATA_READ
, args
, 8, gstats
.data
, pkt
->hdr
.num_sectors
*HDD_SECTOR_SIZE
);
305 // !!! real writes !!!
306 *(u32
*)&args
[0] = pkt
->hdr
.start_sector
;
307 *(u32
*)&args
[4] = pkt
->hdr
.num_sectors
;
308 *(u32
*)&args
[8] = (u32
)gstats
.data
;
309 devctl("hdd0:", APA_DEVCTL_ATA_IOP_WRITE
, args
, 8+(pkt
->hdr
.num_sectors
*HDD_SECTOR_SIZE
), NULL
, 0);
311 ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(struct tcp_packet_header
));
316 // ------------------------------------------------
318 // ------------------------------------------------
320 r
= devctl("hdd0:", APA_DEVCTL_TOTAL_SECTORS
, NULL
, 0, NULL
, 0);
322 pkt
->hdr
.result
= -1;
324 pkt
->hdr
.result
= r
>> 1;
325 ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(struct tcp_packet_header
));
330 // ------------------------------------------------
332 // ------------------------------------------------
335 gstats
.data
= (u8
*)(((long)&gstats
.buffer
+ HDD_SECTOR_SIZE
- 1) & ~(HDD_SECTOR_SIZE
- 1));
337 *(u32
*)&args
[0] = pkt
->hdr
.start_sector
;
338 *(u32
*)&args
[4] = pkt
->hdr
.num_sectors
;
340 if (pkt
->hdr
.num_sectors
> 2) {
341 r
= devctl("hdd0:", APA_DEVCTL_ATA_READ
, args
, 8, gstats
.data
, pkt
->hdr
.num_sectors
*HDD_SECTOR_SIZE
);
343 pkt
->hdr
.result
= -1;
345 pkt
->hdr
.result
= pkt
->hdr
.num_sectors
;
347 ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(struct tcp_packet_header
));
349 ack_tcp_command(tcp_client_socket
, gstats
.data
, pkt
->hdr
.num_sectors
*HDD_SECTOR_SIZE
);
352 r
= devctl("hdd0:", APA_DEVCTL_ATA_READ
, args
, 8, pkt
->data
, pkt
->hdr
.num_sectors
*HDD_SECTOR_SIZE
);
354 pkt
->hdr
.result
= -1;
356 pkt
->hdr
.result
= pkt
->hdr
.num_sectors
;
358 ack_tcp_command(tcp_client_socket
, &tcp_buf
[0], sizeof(tcp_packet_t
));
364 // ------------------------------------------------
366 // ------------------------------------------------
368 devctl("hdd0:", APA_DEVCTL_FLUSH_CACHE
, NULL
, 0, NULL
, 0);
373 // ------------------------------------------------
375 // ------------------------------------------------
377 devctl("hdd0:", APA_DEVCTL_FLUSH_CACHE
, NULL
, 0, NULL
, 0);
378 devctl("hdd0:", APA_DEVCTL_DEV9_SHUTDOWN
, NULL
, 0, NULL
, 0);
379 *((volatile u8
*)0xbf402017) = 0x00;
380 *((volatile u8
*)0xbf402016) = 0x0f;
387 reject_tcp_command(tcp_client_socket
, "handle_recv: unknown command");
398 //-------------------------------------------------------------------------
399 void tcp_server_thread(void *args
)
401 int tcp_socket
, client_socket
= -1;
402 struct sockaddr_in peer
;
408 peer
.sin_family
= AF_INET
;
409 peer
.sin_port
= htons(TCP_SERVER_PORT
);
410 peer
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
413 tcp_socket
= lwip_socket(AF_INET
, SOCK_STREAM
, 0);
418 r
= lwip_bind(tcp_socket
, (struct sockaddr
*)&peer
, sizeof(peer
));
423 r
= lwip_listen(tcp_socket
, 3);
428 peerlen
= sizeof(peer
);
429 // wait for incoming connection
430 client_socket
= lwip_accept(tcp_socket
, (struct sockaddr
*)&peer
, &peerlen
);
431 if (client_socket
< 0)
434 // got connection, handle the client
435 r
= handle_tcp_client(client_socket
);
437 lwip_close(client_socket
);
442 lwip_close(tcp_socket
);
446 //-------------------------------------------------------------------------
447 void udp_server_thread(void *args
)
450 struct sockaddr_in peer
;
452 udp_packet_t
*pkt
= (udp_packet_t
*)udp_buf
;
456 peer
.sin_family
= AF_INET
;
457 peer
.sin_port
= htons(UDP_SERVER_PORT
);
458 peer
.sin_addr
.s_addr
= htonl(INADDR_ANY
);
461 udp_socket
= lwip_socket(AF_INET
, SOCK_DGRAM
, 0);
466 r
= lwip_bind(udp_socket
, (struct sockaddr
*)&peer
, sizeof(peer
));
473 r
= lwip_recv(udp_socket
, udp_buf
, sizeof(udp_buf
), 0);
475 // check to see if it's the command we're expecting
476 if ((r
== sizeof (udp_packet_t
)) && (pkt
->command
== gstats
.command
)) {
478 // check the start sector is valid
479 if ((gstats
.start_sector
<= pkt
->start_sector
) && (pkt
->start_sector
< gstats
.start_sector
+ gstats
.num_sectors
)) {
481 u32 start_sector
= pkt
->start_sector
- gstats
.start_sector
;
483 if (!GETBIT(gstats
.bitmask
, start_sector
)) {
484 mips_memcpy(gstats
.data
+ start_sector
*HDD_SECTOR_SIZE
, pkt
, HDD_SECTOR_SIZE
*2);
486 SETBIT(gstats
.bitmask
, start_sector
);
487 SETBIT(gstats
.bitmask
, start_sector
+ 1);
490 SignalSema(udp_mutex
);
498 lwip_close(udp_socket
);