int netmd_send_message(netmd_dev_handle *devh, unsigned char *cmd, const size_t cmdlen) { unsigned char pollbuf[4]; int len; libusb_device_handle *dev; dev = (libusb_device_handle *)devh; /* poll to see if we can send data */ len = netmd_poll(dev, pollbuf, 1); if (len != 0) { netmd_log(NETMD_LOG_ERROR, "netmd_exch_message: netmd_poll failed\n"); return (len > 0) ? NETMDERR_NOTREADY : len; } /* send data */ netmd_log(NETMD_LOG_DEBUG, "Command:\n"); netmd_log_hex(NETMD_LOG_DEBUG, cmd, cmdlen); if (libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE, 0x80, 0, 0, cmd, (int)cmdlen, NETMD_SEND_TIMEOUT) < 0) { netmd_log(NETMD_LOG_ERROR, "netmd_exch_message: libusb_control_transfer failed\n"); return NETMDERR_USB; } return 0; }
int netmd_recv_message(netmd_dev_handle *devh, unsigned char* rsp) { int len; unsigned char pollbuf[4]; libusb_device_handle *dev; dev = (libusb_device_handle *)devh; /* poll for data that minidisc wants to send */ len = netmd_poll(dev, pollbuf, NETMD_RECV_TRIES); if (len <= 0) { netmd_log(NETMD_LOG_ERROR, "netmd_exch_message: netmd_poll failed\n"); return (len == 0) ? NETMDERR_TIMEOUT : len; } /* receive data */ if (libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE, pollbuf[1], 0, 0, rsp, len, NETMD_RECV_TIMEOUT) < 0) { netmd_log(NETMD_LOG_ERROR, "netmd_exch_message: libusb_control_transfer failed\n"); return NETMDERR_USB; } netmd_log(NETMD_LOG_DEBUG, "Response:\n"); netmd_log_hex(NETMD_LOG_DEBUG, rsp, (size_t)len); /* return length */ return len; }
netmd_error netmd_secure_real_recv_track(netmd_dev_handle *dev, uint32_t length, FILE *file, size_t chunksize) { uint32_t done = 0; unsigned char *data; int status; netmd_error error = NETMD_NO_ERROR; int transferred = 0; data = malloc(chunksize); while (done < length) { if ((length - done) < chunksize) { chunksize = length - done; } status = libusb_bulk_transfer((libusb_device_handle*)dev, 0x81, data, (int)chunksize, &transferred, 10000); if (status >= 0) { done += transferred; fwrite(data, transferred, 1, file); netmd_log(NETMD_LOG_DEBUG, "%.1f%%\n", (double)done/(double)length * 100); } else if (status != -LIBUSB_ERROR_TIMEOUT) { error = NETMD_USB_ERROR; } } free(data); return error; }
/* polls to see if minidisc wants to send data @param dev USB device handle @param buf pointer to poll buffer @param tries maximum attempts to poll the minidisc @return if error <0, else number of bytes that md wants to send */ static int netmd_poll(libusb_device_handle *dev, unsigned char *buf, int tries) { int i; for (i = 0; i < tries; i++) { /* send a poll message */ memset(buf, 0, 4); if (libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE, 0x01, 0, 0, buf, 4, NETMD_POLL_TIMEOUT) < 0) { netmd_log(NETMD_LOG_ERROR, "netmd_poll: libusb_control_transfer failed\n"); return NETMDERR_USB; } if (buf[0] != 0) { break; } if (i > 0) { msleep(1000); } } return buf[2]; }
netmd_error netmd_get_devname(netmd_dev_handle* devh, char* buf, size_t buffsize) { int result; result = usb_get_string_simple((usb_dev_handle *)devh, 2, buf, buffsize); if (result < 0) { netmd_log(NETMD_LOG_ERROR, "usb_get_string_simple failed, %s (%d)\n", strerror(errno), errno); buf[0] = 0; return NETMD_USB_ERROR; } return NETMD_NO_ERROR; }
void netmd_transfer_song_packets(netmd_dev_handle *dev, netmd_track_packets *packets) { netmd_track_packets *p; unsigned char *packet, *buf; size_t packet_size; int error; int transferred = 0; p = packets; while (p != NULL) { /* length + key + iv + data */ packet_size = 8 + 8 + 8 + p->length; packet = malloc(packet_size); buf = packet; /* build packet... */ netmd_copy_quadword_to_buffer(&buf, p->length); memcpy(buf, p->key, 8); memcpy(buf + 8, p->iv, 8); memcpy(buf + 16, p->data, p->length); /* ... send it */ error = libusb_bulk_transfer((libusb_device_handle*)dev, 2, packet, (int)packet_size, &transferred, 10000); netmd_log(NETMD_LOG_DEBUG, "%d %d\n", packet_size, error); /* cleanup */ free(packet); buf = NULL; if (error >= 0) { p = p->next; } break; } }
int main(int argc, char* argv[]) { netmd_dev_handle* devh; minidisc my_minidisc, *md = &my_minidisc; netmd_device *device_list, *netmd; unsigned int i = 0; unsigned int j = 0; char name[16]; uint16_t track, playmode; int c; netmd_time time; netmd_error error; FILE *f; error = netmd_init(&device_list); if (error != NETMD_NO_ERROR) { printf("Error initializing netmd\n%s\n", netmd_strerror(error)); return -1; } if (device_list == NULL) { puts("Found no NetMD device(s)."); return -1; } /* pick first available device */ netmd = device_list; error = netmd_open(netmd, &devh); if(error != NETMD_NO_ERROR) { printf("Error opening netmd\n%s\n", netmd_strerror(error)); return -1; } error = netmd_get_devname(devh, name, 16); if (error != NETMD_NO_ERROR) { printf("Could not get device name\n%s\n", netmd_strerror(error)); return -1; } printf("%s\n", name); netmd_initialize_disc_info(devh, md); printf("Disc Title: %s\n\n", md->groups[0].name); /* by default, log only errors */ netmd_set_log_level(NETMD_LOG_ERROR); /* parse options */ while (1) { c = getopt(argc, argv, "t"); if (c == -1) { break; } switch (c) { case 't': netmd_set_log_level(NETMD_LOG_ALL); break; default: fprintf(stderr, "Unknown option '%c'\n", c); break; } } /* update argv and argc after parsing options */ argv = &argv[optind - 1]; argc -= (optind - 1); /* parse commands */ if(argc > 1) { if(strcmp("rename", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); netmd_cache_toc(devh); netmd_set_title(devh, i & 0xffff, argv[3]); netmd_sync_toc(devh); } else if(strcmp("move", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); j = strtoul(argv[3], NULL, 10); netmd_move_track(devh, i & 0xffff, j & 0xffff); } else if(strcmp("groupmove", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); j = strtoul(argv[3], NULL, 10); netmd_move_group(devh, md, j & 0xffff, i & 0xffff); } else if(strcmp("write", argv[1]) == 0) { if(netmd_write_track(devh, argv[2]) < 0) { fprintf(stderr, "Error writing track %i\n", errno); } } else if(strcmp("newgroup", argv[1]) == 0) { netmd_create_group(devh, md, argv[2]); } else if(strcmp("settitle", argv[1]) == 0) { netmd_cache_toc(devh); netmd_set_disc_title(devh, argv[2], strlen(argv[2])); netmd_sync_toc(devh); } else if(strcmp("group", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); j = strtoul(argv[3], NULL, 10); if(!netmd_put_track_in_group(devh, md, i & 0xffff, j & 0xffff)) { printf("Something screwy happened\n"); } } else if(strcmp("retitle", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); netmd_set_group_title(devh, md, i, argv[3]); } else if(strcmp("play", argv[1]) == 0) { if( argc > 2 ) { i = strtoul(argv[2],NULL, 10); netmd_set_track( devh, i & 0xffff ); } netmd_play(devh); } else if(strcmp("stop", argv[1]) == 0) { netmd_stop(devh); } else if(strcmp("pause", argv[1]) == 0) { netmd_pause(devh); } else if(strcmp("fforward", argv[1]) == 0) { netmd_fast_forward(devh); } else if(strcmp("rewind", argv[1]) == 0) { netmd_rewind(devh); } else if(strcmp("next", argv[1]) == 0) { netmd_track_next(devh); } else if(strcmp("previous", argv[1]) == 0) { netmd_track_previous(devh); } else if(strcmp("restart", argv[1]) == 0) { netmd_track_restart(devh); } else if(strcmp("settime", argv[1]) == 0) { track = strtoul(argv[2], (char **) NULL, 10) & 0xffff; if (argc > 6) { time.hour = strtoul(argv[3], (char **) NULL, 10) & 0xffff; time.minute = strtoul(argv[4], (char **) NULL, 10) & 0xff; time.second = strtoul(argv[5], (char **) NULL, 10) & 0xff; time.frame = strtoul(argv[6], (char **) NULL, 10) & 0xff; } else { time.hour = 0; time.minute = strtoul(argv[3], (char **) NULL, 10) & 0xff; time.second = strtoul(argv[4], (char **) NULL, 10) & 0xff; if (argc > 5) { time.frame = strtoul(argv[5], (char **) NULL, 10) & 0xff;; } else { time.frame = 0; } } netmd_set_time(devh, track, &time); } else if(strcmp("m3uimport", argv[1]) == 0) { import_m3u_playlist(devh, argv[2]); } else if(strcmp("delete", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); netmd_delete_track(devh, i & 0xffff); } else if(strcmp("deletegroup", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); netmd_delete_group(devh, md, i & 0xffff); } else if(strcmp("status", argv[1]) == 0) { print_current_track_info(devh); } else if (strcmp("raw", argv[1]) == 0) { send_raw_message(devh, argv[2]); } else if (strcmp("setplaymode", argv[1]) == 0) { playmode = 0; int i; for (i = 2; i < argc; i++) { if (strcmp(argv[i], "single") == 0) { playmode |= NETMD_PLAYMODE_SINGLE; } else if (strcmp(argv[i], "repeat") == 0) { playmode |= NETMD_PLAYMODE_REPEAT; } else if (strcmp(argv[i], "shuffle") == 0) { playmode |= NETMD_PLAYMODE_SHUFFLE; } } printf("%x\n", playmode); netmd_set_playmode(devh, playmode); } else if (strcmp("capacity", argv[1]) == 0) { netmd_disc_capacity capacity; netmd_get_disc_capacity(devh, &capacity); printf("Recorded: "); print_time(&capacity.recorded); printf("\nTotal: "); print_time(&capacity.total); printf("\nAvailable: "); print_time(&capacity.available); printf("\n"); } else if (strcmp("recv", argv[1]) == 0) { i = strtoul(argv[2], NULL, 10); f = fopen(argv[3], "wb"); netmd_secure_recv_track(devh, i & 0xffff, f); fclose(f); } else if (strcmp("send", argv[1]) == 0) { netmd_error error; netmd_ekb ekb; unsigned char chain[] = {0x25, 0x45, 0x06, 0x4d, 0xea, 0xca, 0x14, 0xf9, 0x96, 0xbd, 0xc8, 0xa4, 0x06, 0xc2, 0x2b, 0x81, 0x49, 0xba, 0xf0, 0xdf, 0x26, 0x9d, 0xb7, 0x1d, 0x49, 0xba, 0xf0, 0xdf, 0x26, 0x9d, 0xb7, 0x1d}; unsigned char signature[] = {0xe8, 0xef, 0x73, 0x45, 0x8d, 0x5b, 0x8b, 0xf8, 0xe8, 0xef, 0x73, 0x45, 0x8d, 0x5b, 0x8b, 0xf8, 0x38, 0x5b, 0x49, 0x36, 0x7b, 0x42, 0x0c, 0x58}; unsigned char rootkey[] = {0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37, 0x13, 0x37}; netmd_keychain *keychain; netmd_keychain *next; size_t done; unsigned char hostnonce[8] = { 0 }; unsigned char devnonce[8] = { 0 }; unsigned char sessionkey[8] = { 0 }; unsigned char kek[] = { 0x14, 0xe3, 0x83, 0x4e, 0xe2, 0xd3, 0xcc, 0xa5 }; unsigned char contentid[] = { 0x01, 0x0F, 0x50, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0xA2, 0x8D, 0x3E, 0x1A, 0x3B, 0x0C, 0x44, 0xAF, 0x2f, 0xa0 }; netmd_track_packets *packets = NULL; size_t packet_count = 0; struct stat stat_buf; unsigned char *data; size_t data_size; uint16_t track; unsigned char uuid[8] = { 0 }; unsigned char new_contentid[20] = { 0 }; error = netmd_secure_leave_session(devh); puts(netmd_strerror(error)); error = netmd_secure_set_track_protection(devh, 0x01); puts(netmd_strerror(error)); error = netmd_secure_enter_session(devh); puts(netmd_strerror(error)); /* build ekb */ ekb.id = 0x26422642; ekb.depth = 9; ekb.signature = malloc(sizeof(signature)); memcpy(ekb.signature, signature, sizeof(signature)); /* build ekb key chain */ ekb.chain = NULL; for (done = 0; done < sizeof(chain); done+=16U) { next = malloc(sizeof(netmd_keychain)); if (ekb.chain == NULL) { ekb.chain = next; } else { keychain->next = next; } next->next = NULL; next->key = malloc(16); memcpy(next->key, chain + done, 16); keychain = next; } error = netmd_secure_send_key_data(devh, &ekb); puts(netmd_strerror(error)); /* cleanup */ free(ekb.signature); keychain = ekb.chain; while (keychain != NULL) { next = keychain->next; free(keychain->key); free(keychain); keychain = next; } /* exchange nonces */ gcry_create_nonce(hostnonce, sizeof(hostnonce)); error = netmd_secure_session_key_exchange(devh, hostnonce, devnonce); puts(netmd_strerror(error)); /* calculate session key */ retailmac(rootkey, hostnonce, devnonce, sessionkey); error = netmd_secure_setup_download(devh, contentid, kek, sessionkey); puts(netmd_strerror(error)); /* read source */ stat(argv[2], &stat_buf); data_size = (size_t)stat_buf.st_size; data = malloc(data_size); f = fopen(argv[2], "rb"); fseek(f, 60, SEEK_CUR); fread(data, data_size - 60, 1, f); fclose(f); error = netmd_prepare_packets(data, data_size-60, &packets, &packet_count, kek); puts(netmd_strerror(error)); /* send to device */ error = netmd_secure_send_track(devh, NETMD_WIREFORMAT_LP2, NETMD_DISKFORMAT_LP2, (data_size - 60) / 192, packets, packet_count, sessionkey, &track, uuid, new_contentid); puts(netmd_strerror(error)); /* cleanup */ netmd_cleanup_packets(&packets); /* set title */ netmd_log(NETMD_LOG_DEBUG, "New Track: %d\n", track); netmd_cache_toc(devh); netmd_set_title(devh, track, "test"); netmd_sync_toc(devh); /* commit track */ error = netmd_secure_commit_track(devh, track, sessionkey); puts(netmd_strerror(error)); /* forget key */ error = netmd_secure_session_key_forget(devh); puts(netmd_strerror(error)); /* leave session */ error = netmd_secure_leave_session(devh); puts(netmd_strerror(error)); } else if(strcmp("help", argv[1]) == 0) { print_syntax(); } else { print_disc_info(devh, md); print_syntax(); } } else print_disc_info(devh, md); netmd_clean_disc_info(md); netmd_close(devh); netmd_clean(&device_list); return 0; }